我在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)
在第一个版本中,您使用相同的空列表对象作为两个键的值,因此如果您更改了一个,则也会更改另一个.
看这个:
>>> 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.如果该值是一个可变对象,并且您更改了该对象,那么您还可以合理地预期会发生什么?