在字典中为一个键添加多个值

172

我是 Python 新手,我有一个年份列表以及每个年份对应的值。我想要做的是检查这些年份是否已经在一个字典中存在,如果存在,就将该值附加到该键对应的值列表中。

例如,我有一个年份列表,每个年份都对应一个值:

2010  
2  
2009  
4  
1989  
8  
2009  
7  

我想要做的是将年份作为键,将那些个位数作为值填入字典中。不过,如果我有两次列出2009年,我希望将第二个值附加到该字典中相应值的列表中,所以我想要:

2010: 2  
2009: 4, 7  
1989: 8  

现在我有以下内容:

d = dict()  
years = []  

(get 2 column list of years and values)

for line in list:    
    year = line[0]   
    value = line[1]  

for line in list:  
    if year in d.keys():  
        d[value].append(value)  
    else:  
        d[value] = value  
        d[year] = year  

1
另一个类似的问题:https://dev59.com/cm435IYBdhLWcg3wfgQf - River
7个回答

240

如果我能改述您的问题,您想要的是一个以年份为键,每个年份都包含与该年相关联的值列表的数组的字典,对吗?这是我的做法:

years_dict = dict()

for line in list:
    if line[0] in years_dict:
        # append the new number to the existing array at this slot
        years_dict[line[0]].append(line[1])
    else:
        # create a new array in this slot
        years_dict[line[0]] = [line[1]]

你最终会得到一个类似于以下内容的年份字典:

{
    "2010": [2],
    "2009": [4,7],
    "1989": [8]
}

通常,创建“平行数组”是不良的编程实践,其中项目通过具有相同索引而隐式关联,而不是作为包含它们两个的容器的适当子级。


20
尽管现代Python安装程序中提供了像dict.setdefault()collections.defaultdict这样的酷炫技巧,但这绝对是正确的方法,尽管可能不是最简洁的方法。 - jathanism
2
如果您使用defaultdict,请将其设置为列表:dd = defaultdict(list)。 - sparrow
1
与其他答案中描述的方法相比,这种方法的性能非常低下。 - Jean-François Fabre

123

最好使用collections.defaultdict(在Python 2.5中添加)。这允许您指定缺少键的默认对象类型(例如list)。

因此,不是首先创建一个不存在的键然后附加到键的值,而是直接附加到不存在的键以获得所需的结果。

以下是使用您的数据的快速示例:

>>> from collections import defaultdict
>>> data = [(2010, 2), (2009, 4), (1989, 8), (2009, 7)]
>>> d = defaultdict(list)
>>> d
defaultdict(<type 'list'>, {})
>>> for year, month in data:
...     d[year].append(month)
... 
>>> d
defaultdict(<type 'list'>, {2009: [4, 7], 2010: [2], 1989: [8]})

这样你就不用担心是否看到了与年份相关的数字。你只需追加并忘记,知道缺少的键总是一个列表。如果键已经存在,则将其追加即可。

4
这是最佳答案。这里的意图是创建一个列表字典,而最简单的方法是使用defaultdict(list)。恭喜! - Emanuel Fontelles
1
非常整洁和清晰的方法。 - Rishabh Sahrawat

57
你可以使用setdefault
for line in list:  
    d.setdefault(year, []).append(value)

这样做是因为 setdefault 返回列表并将其设置在字典上,由于列表是可变的,所以向 setdefault 返回的版本追加元素与直接向字典中的列表追加元素相同。如果有任何不清楚的地方,请告诉我。


27
d = {} 

# import list of year,value pairs

for year,value in mylist:
    try:
        d[year].append(value)
    except KeyError:
        d[year] = [value]

Python的方式是——它比请求许可更容易接受错误并进行纠正!


7
Python 的编程方式是避免重复功能。 - SilentGhost
3
我不明白这里有什么重复。 - Paul Rooney

19

以下是使用 not in 操作符进行此操作的另一种替代方法:

# define an empty dict
years_dict = dict()

for line in list:
    # here define what key is, for example,
    key = line[0]
    # check if key is already present in dict
    if key not in years_dict:
        years_dict[key] = []
    # append some value 
    years_dict[key].append(some.value)

非常甜美,不使用它就太可惜了。你一定要喜欢Python。我喜欢这种技术,因为在我的用例中,它为我提供了更精细的键值管理,具有附加和列表压缩的功能。 - Lenn Dolling

7

如果您将这些值放入元组列表中,那么操作会更加简单。为此,您可以使用列表切片和zip函数。

data_in = [2010,2,2009,4,1989,8,2009,7]
data_pairs = zip(data_in[::2],data_in[1::2])

zip函数可以将任意数量的列表打包成一个元组,例如在此处打包了data_in中的奇数和偶数项。

现在,我们可以使用setdefault方法。

data_dict = {}
for x in data_pairs:
    data_dict.setdefault(x[0],[]).append(x[1])

setdefault 接受一个键和一个默认值,返回关联的值。如果当前没有值,则返回默认值。在这种情况下,我们将得到一个空或已填充的列表,然后将当前值附加到其中。


3
如果你想要一个(几乎)单行代码:
from collections import deque

d = {}
deque((d.setdefault(year, []).append(value) for year, value in source_of_data), maxlen=0)
使用 dict.setdefault,你可以将“检查键是否已存在并在不存在时创建新列表”的思想封装到单个调用中。这允许你编写一个生成器表达式,它被 deque 尽可能有效地消耗,因为队列长度设置为零。deque 将立即被丢弃,结果将在 d 中。
这只是我为了好玩而做的事情,我不建议使用它。在某些时候通过 deque 消耗任意可迭代对象是有时间和地点的,但这绝对不是那种情况。

如果我使用 data = [(2010, 2), (2009, 4), (1989, 8), (2009, 7)],它会返回 deque([]) - Cleb
@Cleb。结果在d中。双端队列应该被丢弃。它的唯一功能是尽快处理生成器。 - Mad Physicist
哎呀,我真是太蠢了;那么它实际上运行得非常好... - Cleb
1
@Cleb。我添加了一个澄清句子。创建一个对象只是为了扔掉它并不直观。我想知道是否可以直接使用__init__方法。类似于deque.__init__(None, iterable, maxlen=0) - Mad Physicist
@Cleb。事实证明,您不能放弃deque对象: TypeError:描述符'__ init__'需要一个'collections.deque'对象,但收到了'NoneType' - Mad Physicist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接