在Python中,如何让用户修改类中字典的值?

1 投票
2 回答
2095 浏览
提问于 2025-04-16 22:41

我之前有个类似的问题,在另一个讨论串里得到了答案。

如何在Python中让用户选择要更新的字典键,然后输入新值来更新字典的值?

简单来说,我想通过用户输入来修改一个嵌套字典的值。我用那个解决方案试了一下,效果很好,但我想用类来写这个程序。所以我创建了一个类,里面有一个方法来编辑字典,基本上用的是相同的代码,但当我在类的方法中运行时,却出现了“键错误”。

在主函数中,上面链接的问题的解决方案运行得很好。但是在类的方法中:

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()

当所有代码都放在一个长函数里时,这个方法是有效的,但看起来很糟糕。我在努力学习更好的面向对象编程(OOP)实践,但我搞不清楚如何通过玩家的名字来调用那个字典以改变它的值。我花了几天时间在复习关于类和字典的教程和问题,但显然我对变量是如何从函数传递到方法中有些误解。

我觉得它甚至无法将字典 vars()[player_name] 赋值给一个变量以便打印出来,这意味着它没有识别出这是在 addplayer 方法中创建的字典。但它仍然在玩家列表中列出那个字典,说明它在那个实例中是存在的。那么为什么在我尝试在 editplayer 方法中访问它时,它却不识别呢?我该如何调用在一个方法中创建的嵌套字典,以便在第二个方法中改变那个字典里的值呢?

Karl 提出了需要澄清的好点子:这是我想要的属性。

self.name - 我想为每个创建的队伍实例化一个名字

self.list of players - 每个队伍应该有自己的球员列表,这些列表是字典,记录着每个球员的统计数据。所以 team1 应该有自己的列表,team2 则有不同的列表等等。

self.position1/2 - 每个队伍的球员会被分类到他们各自的位置字典中。所以像乔·蒙塔纳这样的球员的统计数据字典会在那个队伍的四分卫字典中找到。

self.roster - 应该是按位置分组的队伍名单。所以调用 print team1.roster 应该会打印出按位置分组的球员。

2 个回答

1

首先,出现Key error时,错误信息会告诉你是程序中的哪一行引发了这个错误。如果你看代码时不太明白,可以在那一行之前加一个打印语句,这样就能更清楚了。

其次,你把用户输入当作键来用。用户输入是不可靠的,你肯定会经常遇到键错误,所以你的代码应该处理这种情况。可以用try:except:来捕捉这个错误,或者在使用键查找字典之前,每次都先用if key in mydict:来检查一下。

第三,你用vars()的做法很奇怪。如果你的应用程序使用了全局变量,那么它应该知道这个变量的名字,不需要用vars()来引用。你是不是在某个方法里忘记声明全局变量了?

def method(self,name):
    global bigdict
    bigdict[name] = "set at least one time"
1

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()

仍然没有做到所有的“正确”,但应该给你提供足够的思考材料。

撰写回答