使用fromkeys和可变对象创建字典.惊喜

joa*_*uin 18 python

我在Python 2.6和3.2中遇到了令我惊讶的行为:

>>> xs = dict.fromkeys(range(2), [])
>>> xs
{0: [], 1: []}
>>> xs[0].append(1)
>>> xs
{0: [1], 1: [1]}
Run Code Online (Sandbox Code Playgroud)

然而,dict3.2中的理解表现出更礼貌的举止:

>>> xs = {i:[] for i in range(2)}
>>> xs
{0: [], 1: []}
>>> xs[0].append(1)
>>> xs
{0: [1], 1: []}
>>> 
Run Code Online (Sandbox Code Playgroud)

为什么fromkeys这样做?

And*_*ark 19

您的Python 2.6示例等效于以下内容,这可能有助于澄清:

>>> a = []
>>> xs = dict.fromkeys(range(2), a)
Run Code Online (Sandbox Code Playgroud)

结果字典中的每个条目都将引用同一个对象.正如您所见,通过每个字典条目可以看到变异该对象的效果,因为它是一个对象.

>>> xs[0] is a and xs[1] is a
True
Run Code Online (Sandbox Code Playgroud)

使用字典理解,或者如果你坚持使用Python 2.6或更早版本并且你没有字典理解,你可以通过使用dict()生成器表达式来获得字典理解行为:

xs = dict((i, []) for i in range(2))
Run Code Online (Sandbox Code Playgroud)

  • 生成的`xs`字典中的每个条目都将引用与其值相同的`a`对象,无论"a"是否恰好是可变的.但是当然OP中的问题只有在`a`是可变的并且你改变它时才会出现. (2认同)

Tim*_*ker 5

在第一个版本中,您使用相同的空列表对象作为两个键的值,因此如果您更改了一个,则也会更改另一个.

看这个:

>>> empty = []
>>> d = dict.fromkeys(range(2), empty)
>>> d
{0: [], 1: []}
>>> empty.append(1) # same as d[0].append(1) because d[0] references empty!
>>> d
{0: [1], 1: [1]}
Run Code Online (Sandbox Code Playgroud)

在第二个版本中,在dict理解的每次迭代中都会创建一个新的空列表对象,因此它们彼此独立.

关于"为什么"这样的fromkeys()工作 - 嗯,如果它不那样工作将是令人惊讶的.fromkeys(iterable, value)使用来自iterable的键构造一个新的dict,它们都具有值value.如果该值是一个可变对象,并且您更改了该对象,那么您还可以合理地预期会发生什么?

  • 蒂姆,我理解为什么会这样.我的问题更像是"它为什么设计成这样的行为?".对不起,问题不明确. (3认同)