如何在 Python 中初始化空列表字典?

How do I initialize a dictionary of empty lists in Python?

提问人:Martin Burch 提问时间:7/17/2012 最后编辑:Martin Burch 更新时间:1/22/2023 访问量:169093

问:

我尝试以编程方式创建列表字典,但未能允许我单独处理字典键。每当我创建列表字典并尝试附加到一个键时,所有列表都会更新。下面是一个非常简单的测试用例:

data = {}
data = data.fromkeys(range(2),[])
data[1].append('hello')
print data

实际结果:{0: ['hello'], 1: ['hello']}

预期结果:{0: [], 1: ['hello']}

这是有效的方法

data = {0:[],1:[]}
data[1].append('hello')
print data

实际和预期结果:{0: [], 1: ['hello']}

为什么该方法无法按预期工作?fromkeys

python 列表 字典

评论

0赞 SomethingSomething 6/8/2022
我认为您每次都应该调用以实际创建一个新列表。您可能希望使用字典推导式而不是list()fromkeys

答:

123赞 Martijn Pieters 7/17/2012 #1

尝试改用 defaultdict

from collections import defaultdict
data = defaultdict(list)
data[1].append('hello')

这样,就不需要提前使用空列表初始化密钥。相反,该对象在每次访问尚不存在的键时调用给定给它的工厂函数。因此,在此示例中,尝试在内部访问触发器,并为该键提供一个新的空列表作为其值。defaultdict()data[1]data[1] = list()

原始代码共享一个(可变)列表。同样地.fromkeys

alist = [1]
data = dict.fromkeys(range(2), alist)
alist.append(2)
print(data)

将输出.这在 dict.fromkeys() 文档中被调用{0: [1, 2], 1: [1, 2]}

所有值都只引用单个实例,因此 value 是可变对象(如空列表)通常没有意义。

另一种选择是使用 dict.setdefault() 方法,该方法在首先检查键是否存在后检索该键的值,如果不存在,则设置默认值。 然后可以对结果进行调用:.append

data = {}
data.setdefault(1, []).append('hello')

最后,要从已知键列表和给定的“模板”列表(其中每个值应以相同的元素开头,但是一个不同的列表)创建字典,请使用字典推导并复制初始列表:

alist = [1]
data = {key: alist[:] for key in range(2)}

在这里,创建一个 的浅拷贝,这是为每个值单独完成的。请参阅如何克隆列表,使其在分配后不会意外更改?,以获取复制列表的更多技术。alist[:]alist

9赞 cobie 7/17/2012 #2

您正在使用对单个列表的引用来填充字典,因此当您更新它时,更新将反映在所有引用中。尝试使用字典理解。请参阅在 Python 中创建具有列表推导式的字典

d = {k : v for k in blah blah blah}

评论

0赞 John 8/21/2016
关于初始化字典值的好建议...谢谢科比!我扩展了您的示例以重置现有字典 d 中的值。我按如下方式执行此操作:d = { k:0 for k in d }
0赞 Dr_Zaszuś 5/28/2020
这个答案是什么?v
147赞 Sven Marnach 7/17/2012 #3

当作为第二个参数传递给 时,结果中的所有值都将是同一个对象。[]dict.fromkeys()dictlist

在 Python 2.7 或更高版本中,请改用字典推导式

data = {k: [] for k in range(2)}

在早期版本的 Python 中,没有字典推导式,但可以将列表推导式传递给构造函数:dict

data = dict([(k, []) for k in range(2)])

在 2.4-2.6 中,也可以将生成器表达式传递给 ,并且周围的括号可以去掉dict

data = dict((k, []) for k in range(2))
10赞 g.d.d.c 3/20/2013 #4

您可以使用以下命令:

l = ['a', 'b', 'c']
d = dict((k, [0, 0]) for k in l)
45赞 Blender 3/20/2013 #5

您可以使用字典推导式:

>>> keys = ['a','b','c']
>>> value = [0, 0]
>>> {key: list(value) for key in keys}
    {'a': [0, 0], 'b': [0, 0], 'c': [0, 0]}

评论

0赞 PM 2Ring 7/14/2018
value[:]那么丑(除非你和亚历克斯·马泰利的审美意识:)一样),而且打字也少了。在最新版本的 Python 中,现在有一个方法。就性能而言,小列表(最多 50 或 60 个项目)的切片速度最快,但大型列表的切片速度实际上更快一些。 似乎具有与 类似的性能。对于大型列表,所有 3 种技术都会大大减慢速度:在我的旧 32 位机器上,发生在 32k 左右,YMMV 取决于 CPU 的字大小和缓存大小。list.copylist(value)value.copy()list(value)
44赞 Shawn Mehan 6/13/2017 #6

这个答案是为了向任何对他们尝试使用可变默认值实例化 with 的结果感到困惑的人解释这种行为。dictfromkeys()dict

考虑:

#Python 3.4.3 (default, Nov 17 2016, 01:08:31) 

# start by validating that different variables pointing to an
# empty mutable are indeed different references.
>>> l1 = []
>>> l2 = []
>>> id(l1)
140150323815176
>>> id(l2)
140150324024968

因此,任何更改都不会影响,反之亦然。 到目前为止,任何可变的 Mutable 都是如此,包括 .l1l2dict

# create a new dict from an iterable of keys
>>> dict1 = dict.fromkeys(['a', 'b', 'c'], [])
>>> dict1
{'c': [], 'b': [], 'a': []}

这可能是一个方便的功能。 在这里,我们为每个键分配一个默认值,该默认值也恰好是一个空列表。

# the dict has its own id.
>>> id(dict1)
140150327601160

# but look at the ids of the values.
>>> id(dict1['a'])
140150323816328
>>> id(dict1['b'])
140150323816328
>>> id(dict1['c'])
140150323816328

事实上,他们都使用相同的参考! 对一个的改变就是对所有人的改变,因为它们实际上是同一个对象!

>>> dict1['a'].append('apples')
>>> dict1
{'c': ['apples'], 'b': ['apples'], 'a': ['apples']}
>>> id(dict1['a'])
>>> 140150323816328
>>> id(dict1['b'])
140150323816328
>>> id(dict1['c'])
140150323816328

对于许多人来说,这不是本意!

现在,让我们尝试对用作默认值的列表进行显式复制。

>>> empty_list = []
>>> id(empty_list)
140150324169864

现在创建一个带有 副本的字典。empty_list

>>> dict2 = dict.fromkeys(['a', 'b', 'c'], empty_list[:])
>>> id(dict2)
140150323831432
>>> id(dict2['a'])
140150327184328
>>> id(dict2['b'])
140150327184328
>>> id(dict2['c'])
140150327184328
>>> dict2['a'].append('apples')
>>> dict2
{'c': ['apples'], 'b': ['apples'], 'a': ['apples']}

还是没有喜悦! 我听到有人喊,那是因为我用了一个空名单!

>>> not_empty_list = [0]
>>> dict3 = dict.fromkeys(['a', 'b', 'c'], not_empty_list[:])
>>> dict3
{'c': [0], 'b': [0], 'a': [0]}
>>> dict3['a'].append('apples')
>>> dict3
{'c': [0, 'apples'], 'b': [0, 'apples'], 'a': [0, 'apples']}

的默认行为是赋值。fromkeys()None

>>> dict4 = dict.fromkeys(['a', 'b', 'c'])
>>> dict4
{'c': None, 'b': None, 'a': None}
>>> id(dict4['a'])
9901984
>>> id(dict4['b'])
9901984
>>> id(dict4['c'])
9901984

事实上,所有的值都是相同的(也是唯一的! 现在,让我们以无数种方式之一进行迭代,并更改值。Nonedict

>>> for k, _ in dict4.items():
...    dict4[k] = []

>>> dict4
{'c': [], 'b': [], 'a': []}

嗯,看起来和以前一样!

>>> id(dict4['a'])
140150318876488
>>> id(dict4['b'])
140150324122824
>>> id(dict4['c'])
140150294277576
>>> dict4['a'].append('apples')
>>> dict4
>>> {'c': [], 'b': [], 'a': ['apples']}

但它们确实是不同的,在这种情况下,这是预期的结果。[]

评论

8赞 lucid_dreamer 1/25/2018
所以我们必须迭代吗?
2赞 Ricky Levi 11/30/2021
我认为重点是不要迭代......那是捷径,不然为什么我一开始就需要这个功能呢?
0赞 Karl Knechtel 1/21/2023
当然,你必须迭代。 首先进行迭代。无论如何,这似乎有很多细节来解释一些从前两个答案中已经很清楚的事情。dict.fromkeys