这个答案的目的是为任何被尝试使用可变默认值在dict
中实例化fromkeys()
而获得的结果所困扰的人解释此行为。
考虑:
>>> l1 = []
>>> l2 = []
>>> id(l1)
140150323815176
>>> id(l2)
140150324024968
所以对 l1
的任何更改都不会影响 l2
,反之亦然。
目前为止,对于任何可变对象,包括 dict
都是如此。
# 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
仍然没有成功!
我听到有人喊道,这是因为我使用了一个空列表!
>>> not_empty_list = [0]
>>> dict3 = dict.fromkeys(['a', 'b', 'c'], not_empty_list[:])
>>> dict3
>>> dict3['a'].append('apples')
>>> dict3
< p >
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
实际上,所有的值都是(也是唯一的)None
。现在,让我们以无数种方式之一迭代遍历 dict
并更改其值。
>>> 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']}
但它们确实是不同的[]
,在这种情况下这是预期结果。
list()
来创建一个新的列表。你可能想使用字典推导式而不是fromkeys
。 - SomethingSomething