如何制作包含两个列表和列表索引的字典?
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']
都是相同的对象。当您向其中一个添加新项目时,它会应用于另一个(因为它们是同一个对象)。
或者,您可以使用 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
。
这里有两个问题:首先,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}}
我有两个平行的数据列表,例如:
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 toNone
. 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']
都是相同的对象。当您向其中一个添加新项目时,它会应用于另一个(因为它们是同一个对象)。
或者,您可以使用 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
。
这里有两个问题:首先,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}}