如何制作包含两个列表和列表索引的字典?

How to make a dictionary with two lists and index of lists?

我有两个平行的数据列表,例如:

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 值都被添加到两种流派中,而不是只被添加到与数字对应的流派中。

为什么会出现这种情况?我该如何解决这个问题?

使用:

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}}

尝试将 album = dict.fromkeys(genres,dict()) 替换为

album = {genre: {} for genre in genres}

dict.fromkeys 不工作的原因记录在 doc:

fromkeys() is a class method that returns a new dictionary. value defaults to None. All of the values refer to just a single instance, so it generally doesn’t make sense for value to be a mutable object such as an empty list. To get distinct values, use a dict comprehension instead.

也就是写album = dict.fromkeys(genres,dict())时,album['classic']album['pop']都是相同的对象。当您向其中一个添加新项目时,它会应用于另一个(因为它们是同一个对象)。


或者,您可以使用 defaultdictzip:

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

这里有两个问题:首先,for key,value in album.items(): 循环是多余的,虽然这不会导致问题,因为字典有唯一的键——你将每个 key-value 对存储两次,但是第二次将取代第一次。

重要的问题是在album = dict.fromkeys(genres,dict())之后,album中的两个值会同一个字典。 dict() 发生在 调用 dict.fromkeys 之前,结果对象被传入。dict.fromkeys() 使用那个 相同的对象 作为每个键的值 - 它 而不是 进行复制。

要解决这个问题,请改用字典理解来创建字典:

album = {g: {} for g in genres}

这是一个与 List of lists changes reflected across sublists unexpectedly 类似的问题,不同之处在于它不是 list-of-lists,而是 dict-with-dict 值,我们不是通过乘法创建有问题的数据,而是使用一个方法。然而,底层逻辑是相同的,自然解决方案也以相同的方式工作。

另一种方法是仅在首次需要时才在 album 中创建 key-value 对,首先检查它们是否存在。

另一种方法是使用一种工具来自动创建 on-demand - 例如,来自标准库 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}}