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

我尝试通过编程方式创建列表字典,但是无法单独地址字典键。每当我创建列表字典并试图附加到一个键时,所有列表都会更新。下面是一个非常简单的测试用例:

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

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

Expected result: {0: [], 1: ['hello']}

这样行得通

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

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

Why is the fromkeys method not working as expected?

155739 次浏览

您可以使用 违约判决对象:

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

This way you don't have to initialize all the keys you want to use to lists beforehand. The defaultdict() object instead calls the factory function given to it (list in the above example), every time a key is accessed that doesn't exist yet. So, in the above example, data[1] triggers data[1] = list() internally, giving each key their own list object as the value.

在您的示例中使用的是 (可变)列表:

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

将输出 {0: [1, 2], 1: [1, 2]},这是 called out in the dict.fromkeys() documentation:

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

另一种选择是使用 dict.setdefault(),它在首先检查键是否存在并设置默认值(如果不存在)之后检索键的值。你可以通过 .append()调用来连接:

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

最后,如果必须从已知键列表和带有值的给定列表创建字典,可以使用字典理解和 收到初始列表:

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

alist[:]表达式使用相同的值创建一个新列表(不复制这些值,这是一个浅表复制操作)。

您正在使用对单个列表的引用填充字典,因此当您更新该列表时,更新将反映在所有引用中。试试字典理解吧。你看 用 Python 创建一个带列表内涵的字典

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

[]作为第二个参数传递给 dict.fromkeys()会得到一个相当无用的结果—— dictionary 中的所有值都将是 same列表对象。

在 Python 2.7或以上版本中,你可以使用字典理解来代替:

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

在 Python 的早期版本中,您可以使用

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

你可以用这个:

l = ['a', 'b', 'c']
d = dict((k, [0, 0]) for k in l)

You could use a dict comprehension:

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

This answer is here to explain this behavior to anyone flummoxed by the results they get of trying to instantiate a dict with fromkeys() with a mutable default value in that 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

所以对 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

事实上,他们使用的都是同一个裁判! A change to one is a change to all, since they are in fact the same object!

>>> 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的副本创建一个 dict。

>>> 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

实际上,所有值都是相同的(也是唯一的!) 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']}

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

你可以用这个:

data[:1] = ['hello']