我有两个并行的数据列表,如:
genres = ["classic", "pop", "classic", "classic", "pop"]
plays = [500, 600, 150, 800, 2500]
我想要得到这个结果:
album = {"classic":{0:500, 2:150, 3:800}, "pop":{1:600, 4:2500}} # want to make
所以我试了这个代码:
album = dict.fromkeys(genres,dict())
# album = {'classic': {}, 'pop': {}}
for i in range(len(genres)):
for key,value in album.items():
if genres[i] == key:
album[key].update({i:plays[i]})
album
的结果错误。它看起来像
{'classic': {0: 500, 1: 600, 2: 150, 3: 800, 4: 2500},
'pop': {0: 500, 1: 600, 2: 150, 3: 800, 4: 2500}}
也就是说,每个plays
值都被添加到两个类型中,而不是只添加到与该数字对应的类型中。
为什么会出现这种情况?我该如何解决这个问题?
尝试用
替换album = dict.fromkeys(genres,dict())
album = {genre: {} for genre in genres}
您的dict.fromkeys
不工作的原因记录在文档中:
也就是说,当您编写
fromkeys()
是一个返回新字典的类方法。value默认为None
。所有的值都只引用一个实例,所以value作为一个可变对象(如空列表)通常是没有意义的。若要获得不同的值,请使用字典推导式。
album = dict.fromkeys(genres,dict())
、album['classic']
和album['pop']
时,它们都是相同的对象。当您向其中一个添加新项时,它将应用于另一个(因为它们是相同的对象)。
或者,您可以使用defaultdict
和zip
:
from collections import defaultdict
genres = ["classic", "pop", "classic", "classic", "pop"]
plays = [500, 600, 150, 800, 2500]
album = defaultdict(dict)
for i, (genre, play) in enumerate(zip(genres, plays)):
album[genre][i] = play
print(dict(album))
# {'classic': {0: 500, 2: 150, 3: 800}, 'pop': {1: 600, 4: 2500}}
dict(album)
在大多数情况下是冗余的;你可以像使用字典一样使用album
。
使用说明:
In [1059]: d = {}
In [1060]: for c,i in enumerate(genres):
...: if i in d:
...: d[i].update({c:plays[c]})
...: else:
...: d[i] = {c:plays[c]}
...:
In [1061]: d
Out[1061]: {'classic': {0: 500, 2: 150, 3: 800}, 'pop': {1: 600, 4: 2500}}
这里有两个问题:首先,for key,value in album.items():
循环是冗余的,尽管这不会造成问题,因为字典有唯一的键-您将存储每个键-值对两次,但第二次只会替换第一次。
album = dict.fromkeys(genres,dict())
之后,album
中的两个值将是相同的字典。dict()
在调用dict.fromkeys
之前发生,并传入结果对象。dict.fromkeys()
使用相同的对象作为每个键的值——它不复制。
要解决这个问题,可以使用字典推导来创建字典:
album = {g: {} for g in genres}
这是一个类似于意外地跨子列表反映列表的列表更改的问题,不同的是,它不是列表的列表,而是带有字典值的字典,并且不是通过乘法创建有问题的数据,而是使用方法创建它。然而,底层逻辑是相同的,自然的解决方案也以相同的方式工作。
另一种方法是在album
中只在第一次需要时创建键值对,首先检查它们是否存在。
另一种方法是使用自动按需创建的工具——例如,从标准库collections
模块中创建defaultdict
。这种方式看起来像:
from collections import defaultdict
# other code until we get to:
album = defaultdict(dict)
# whenever we try `album[k].update(v)`, if there is not already an
# `album[k]`, it will automatically create `album[k] = dict()` first
# - with a new dictionary, created just then.
@j1-lee回答正确,但如果您想避免使用defaultdict而使用原始字典,下面是代码。
genres = ["classic", "pop", "classic", "classic", "pop"]
plays = [500, 600, 150, 800, 2500]
all_genres_plays = zip(genres, plays)
album = {}
for index, single_genre_play in enumerate(all_genres_plays):
genre, play = single_genre_play
if genre not in album:
album[genre] = {}
album[genre][index] = play
print(album)
输出:
{'classic': {0: 500, 2: 150, 3: 800}, 'pop': {1: 600, 4: 2500}}