В питоне. Как заставить пользователя изменить значение словаря, когда этот словарь находится в классе?

Итак, у меня был аналогичный вопрос, на который был дан ответ в другой теме.

Как мне обновить значение словаря, когда пользователь выбирает ключ для обновления, а затем новое значение в Python?

По сути, как изменить значение вложенного словаря с помощью raw_input. Я использовал решение, и оно работало хорошо, но я хотел написать программу, используя классы. Итак, я создал класс с методом редактирования словаря, используя по существу тот же код, однако, когда я пытаюсь запустить его в методе класса, он теперь дает мне «ключевую ошибку».

Таким образом, в основной функции это работает, решение в связанном выше вопросе отлично работает. Но в методе класса:

class team: # create a class where each team will be an instance
  def __init__(self, name):
    self.name = name #name of team will be passed from main
    self.list_of_players = [] # create a list of the players
    self.position1 = {} # create a dictionary for each of the positions on that team
    self.position2 = {}
    self.roster = [self.position1, self.position2]

  def addplayer(self, player_name): # the name of the player is passed to this method from main
    print 'add stats' # fill out the appropriate stats through raw_input 
    stat1 = raw_input('stat1: ')
    stat2 = raw_input('stat2: ')
    pos = raw_input('POS: ')
    vars()[player_name] = {'stat1' : stat1, 'stat2' : stat2, 'POS' : pos} #create a dictionary 
    # for the player where all his stats are kept
    player = {player_name : vars()[player_name]} # create a dictionary that will show the 
    # player's name as a string and his stats which are held in the dictionary named after him
    self.list_of_players.append(player) # append the new player to the list of players
    if pos == 'p1': # add the player and his stats to the appropriate position on the team
      self.position1[player_name] = player
    elif pos == 'p2':
      self.position2[player_name] = player
    else:
      pass

  def editplayer(self, player_name): # player's name is passed to the edit function from main
    print self.list_of_players # player's name shows up in the list of players for the team
    edit_stat = raw_input('which stat? ') # choose which stat(key) to edit via raw input
    new_value = raw_input('new value: ') # choose the new value to apply to the chosen key
    vars()[player_name][edit_stat] = new_value # here is where it gives a key error! this worked 
 #in fact even trying to call and print the players name gives the key error. 
    #player = vars()[player_name]
    #print player

def main(): # the main function
  loop1 = 0 # creating a loop so one can come back and edit the teams after creating them
  list_of_teams = [] # initializing list of teams
  while loop1 < 1:
    print list_of_teams # show the user what teams are available to choose from
    team_option = raw_input('new team or old: ') # create a new team or work with an old one
    if team_option == 'new':
      team_name = raw_input('team name? ') # get the team name from raw_input
      vars()[team_name] = team(team_name) #create an instance of this team name
      list_of_teams.append(team_name) # add the team to the list
    else:
      team_name = raw_input('which team? ') # choose which existing team to work with
      player_choice = raw_input('new player or old? ') # choose to create or edit existing player
      player_name = raw_input('player_name? ') # choose which player from raw_input
      if player_choice == 'new':
        vars()[team_name].addplayer(player_name) # give player_name to addplayer method 
        print vars()[team_name].list_of_players # shows the new player in the appropriate
        # instance's roster. This method seems to be working fine
      else:
        vars()[team_name].editplayer(player_name) # gives the player's name to the editplayer
        # method for the appropriate instance.  But the player name just raises a key error in
        # edit player method.  I am baffled.
        print vars()[team_name].list_of_players
if __name__ == '__main__':
  main()

Когда это была одна длинная функция, это работало, но выглядело катастрофой. Пытаюсь изучить лучшие практики ООП, но не могу понять, как вызвать этот словарь по имени игрока, чтобы изменить значение. Последние несколько дней я просматривал учебники и вопросы по классам и словарям, но явно что-то не понимаю в том, как переменные передаются из функции в методы.

Тот факт, что он даже не присваивает словарь vars()[player_name] переменной для вывода, означает, что он не распознает его как словарь, созданный в методе addplayer, я думаю. Но тот факт, что он по-прежнему указывает этот словарь в списке игроков, означает, что он существует в этом экземпляре. Так почему же он не распознает его, когда я пытаюсь обратиться к нему в методе editplayer? И как мне вызвать встроенный словарь, созданный в одном методе, чтобы изменить значение в этом словаре во втором методе?

Карл указал на хорошие моменты, требующие уточнения: Вот какие атрибуты мне нужны.

self.name- мне нужен экземпляр для каждой созданной команды

self.list of player - у каждой команды должен быть свой список игроков, которые являются словарями, содержащими статистику этих людей. поэтому у team1 должен быть свой список. team2 другой список и т.д.

self.position1/2 — игроки каждой команды будут занесены в их различные словари позиций. так что словарь статистики игрока Джо Монтаны будет найден в словаре защитников этой команды.

self.roster - должен быть составом этой команды, сгруппированным по позициям. Таким образом, вызов команды print team1.roster должен вывести этих игроков, сгруппированных по позициям.


person Nathan    schedule 02.08.2011    source источник
comment
Сначала начните с объяснения, какие именно данные вы хотите получить в каждом атрибуте.   -  person Karl Knechtel    schedule 02.08.2011
comment
self.name - я хочу создать экземпляр для каждой команды. Я создаю self.list игроков - каждая команда должна иметь свой собственный список игроков, которые являются словарями, содержащими статистику этих людей. поэтому у team1 должен быть свой список. team2 другой список и т. д. self.position1/2 — игроки каждой команды будут храниться в их различных словарях позиций. поэтому словарь статистики Player joe montana будет найден в словаре защитников этой команды self.roster - должен быть составом этой команды, сгруппированным по позициям. Таким образом, вызов команды print team1.roster должен вывести этих игроков, сгруппированных по позициям.   -  person Nathan    schedule 02.08.2011
comment
Похоже, что вы действительно пытаетесь установить двунаправленное сопоставление между игроками и их позициями...   -  person Karl Knechtel    schedule 02.08.2011


Ответы (2)


1) vars() — это словарь локальных переменных внутри функции.

Когда вы работаете с методом Python, содержимое объекта, для которого вы вызвали метод, не являются локальными переменными. Вот почему у вас должен быть параметр self.

Если вы хотите найти игроков по именам, сделайте это. У вас не список игроков, а список игроков.

2) vars() - это то, что вы почти никогда не должны использовать. Он используется для того, чтобы вы могли представить, что строка является именем переменной. Вам не нужно делать это для всего, что вы здесь делаете. На самом деле вам вообще не нужна переменная в большинстве мест, где вы ее используете. Здесь вам предстоит узнать больше, чем просто ООП.

Рассмотрим эту часть, например:

vars()[team_name] = team(team_name)
list_of_teams.append(team_name)

Вместо того, чтобы пытаться вспомнить команду по имени в vars(), снова найдите команды по имени. Имейте список команд вместо списка. Чтобы получить названия команд, можно просто распечатать ключи словаря.

Простое лучше, чем сложное. Создание переменных на лету сложно. Пользоваться словарями просто.


Я ненавижу кормить с ложки столько кода, но кажется, что это единственный способ получить идею (ы - я действительно не сказал всего выше) за это время:

# Just like we want a class to represent teams, since those are "a thing" in our
# program, we want one for each player as well.

class player(object):
  __slots__ = ['name', 'stats', 'pos']
  def __init__(self, name, stats, pos):
    self.name = name
    self.stats = stats
    self.pos = pos


# Asking the user for information to create an object is not the responsibility of
# that class. We should use external functions for this.
def create_player(name):
  print 'add stats' # fill out the appropriate stats through raw_input 
  stat1 = raw_input('stat1: ')
  stat2 = raw_input('stat2: ')
  pos = raw_input('POS: ')
  # Now we create and return the 'player' object.
  return player(name, {'stat1': stat1, 'stat2': stat2}, pos)


class team(object):
  __slots__ = ['name_to_player', 'position_to_player']
  def __init__(self):
    # We don't make any lists, just dicts, because we want to use them primarily
    # for lookup. Notice how I've named the attributes. In particular, I **don't**
    # talk about type names. That's just an implementation detail. What we care about
    # is how they work: you put a name in, get a player out.
    self.name_to_player = {}
    self.position_to_player = {}

  # Again, we don't ask the questions here; this just actually adds the player.
  def add_player(self, player):
    self.name_to_player[player.name] = player
    self.position_to_player[player.pos] = player

  # Again, we don't ask the questions here; this just does the actual edit.
  def edit_player(self, name, stat, new_value):
    self.name_to_player[name].stats[stat] = new_value


def main(): # the main function
  teams = {} # dict from team name to team object.
  while True:
    print teams.keys()
    # Your human interface was needlessly awkward here; you know from the supplied name
    # whether it's a new team or an old one, because it will or won't be in your
    # existing set of teams. Similarly for players.
    team_name = raw_input('team name? ')
    if team_name not in teams.keys():
      teams[team_name] = team() # create a new team
    else: # edit an existing one
      team = teams[team_name]
      player_name = raw_input('player name? ')
      if player_name in team.name_to_player.keys(): # edit an existing player
        stat = raw_input("stat? ")
        value = raw_input("value? ")
        team.edit_player(player_name, stat, value)
      else: # add a new player
        team.add_player(create_player(player_name))

if __name__ == '__main__':
  main()

Это по-прежнему не делает все "правильно", но это должно дать вам более чем достаточно для размышлений на данный момент.

person Karl Knechtel    schedule 02.08.2011
comment
конечно, мне нужно узнать намного больше, чем просто ОО. Вот почему я здесь. В этом примере происходят две разные вещи. У меня есть список команд в main(), в который я добавляю team_name. Проблема в другом вопросе, который я связал, заключалась в том, что пользовательский ввод был строкой, когда я хотел, чтобы пользователь идентифицировал словарь. Решением этого было vars(). Я не хочу передавать str 'player', потому что, когда я затем пытаюсь вызвать словарь, он выдает мне ошибку 'str' объект не поддерживает назначение элемента. - person Nathan; 02.08.2011
comment
вот почему я использовал var(). Я хочу, чтобы пользователь вводил строковую информацию, но программа интерпретировала ее как имя словаря. - person Nathan; 02.08.2011
comment
просто чтобы уточнить, и, может быть, вы уже это понимаете, и я просто упускаю из виду вашу мысль. Но чтобы уточнить, в словаре нет команд, пока пользователь не создаст их. Пользователь должен указать программе имя переменной dict, он не может просто передать строковый ключ, чтобы вызвать соответствующее значение. Если вы уже поняли, что извините. - person Nathan; 02.08.2011
comment
спасибо за все время, которое вы потратили, пытаясь помочь. Я проработаю то, что вы кормили с ложечки, и посмотрю, смогут ли идеи дойти до меня. - person Nathan; 02.08.2011

Прежде всего, трассировка, которая сопровождает Key error, скажет вам, какая строка в вашей программе вызвала ее, и если это не очевидно из просмотра кода, то вставка оператора печати перед этой строкой должна сделать это очевидным.

Во-вторых, вы используете пользовательский ввод в качестве ключа. Пользовательский ввод ненадежен. У вас БУДУТ все время возникать ключевые ошибки, поэтому ваш код должен иметь дело с этим, либо используя try: except: для перехвата исключения, либо проверяя каждый раз, используя if key in mydict:, перед фактическим использованием ключа для поиска в словаре. .

В-третьих, то, что вы делаете с vars(), очень и очень странно. Если ваше приложение использует глобальную переменную, оно должно знать ее имя и не должно обращаться к vars. Вы забыли объявить глобальную переменную в каком-то методе?

def method(self,name):
    global bigdict
    bigdict[name] = "set at least one time"
person Michael Dillon    schedule 02.08.2011
comment
Это должно быть, конечно, try:...except KeyError:. «Голые» исключения могут скрывать все виды ошибок. - person MRAB; 02.08.2011
comment
Определенно! Я не собирался, кроме как использовать буквально, просто как ссылку на руководство. - person Michael Dillon; 02.08.2011
comment
@ Майкл. Прежде всего спасибо за ответ и попытку помочь. Я очень ценю это. Я указал строку, в которой ошибка, а также показал, где я пытался просто вызвать и напечатать (это в закомментированном разделе и в конце функции редактирования. Я понимаю, что пользовательский ввод нужно будет поймать, однако, когда я я сам просматриваю его, пытаясь дать точный ввод, он не распознает идентичное имя, данное как то, которым вызывается словарь Итак, я думаю, что моя проблема заключается где-то в том, что один метод не распознает словарь в другом? - person Nathan; 02.08.2011
comment
я также запустил if... in list:... else напечатайте «отрицательный», и он напечатает «отрицательный». Я сделал: распечатать список... если в списке: иначе: напечатать "отрицательный", и он распечатает список, показывающий имя словаря... и распечатает "отрицательный" - person Nathan; 02.08.2011
comment
Обычный способ указать, где произошла ошибка, — включить трассировку, которую распечатал Python. - person Michael Dillon; 02.08.2011