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

2024-12-18 08:38:00
admin
原创
71
摘要:问题描述:我尝试以编程方式创建列表字典,但无法单独处理字典键。每当我创建列表字典并尝试附加到一个键时,所有键都会更新。这是一个非常简单的测试用例:data = {} data = data.fromkeys(range(2),[]) data[1].append('hello') print data 实际结...

问题描述:

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

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方法没有按预期发挥作用?


解决方案 1:

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

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

解决方案 2:

尝试使用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)

将输出{0: [1, 2], 1: [1, 2]}。文档中提到了dict.fromkeys()这一点:

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

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

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

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

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

这里,alist[:]创建了 的浅拷贝alist,并且对每个值分别执行此操作。有关复制列表的更多技术,请参阅如何克隆列表以使其在分配后不会意外更改?

解决方案 3:

您可以使用字典理解:

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

解决方案 4:

dict这个答案是为了向那些对尝试使用fromkeys()可变默认值实例化所得到的结果感到困惑的人解释这种行为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

事实上,它们都使用相同的引用!更改一个引用就意味着更改所有引用,因为它们实际上是同一个对象!

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

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

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

解决方案 5:

你可以使用这个:

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

解决方案 6:

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

d = {k : v for k in blah blah blah}
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   984  
  在项目管理领域,CDCP(Certified Data Center Professional)认证评审是一个至关重要的环节,它不仅验证了项目团队的专业能力,还直接关系到项目的成功与否。在这一评审过程中,沟通技巧的运用至关重要。有效的沟通不仅能够确保信息的准确传递,还能增强团队协作,提升评审效率。本文将深入探讨CDCP...
华为IPD流程   0  
  IPD(Integrated Product Development,集成产品开发)是一种以客户需求为核心、跨部门协同的产品开发模式,旨在通过高效的资源整合和流程优化,提升产品开发的成功率和市场竞争力。在IPD培训课程中,掌握关键成功因素是确保团队能够有效实施这一模式的核心。以下将从五个关键成功因素展开讨论,帮助企业和...
IPD项目流程图   0  
  华为IPD(Integrated Product Development,集成产品开发)流程是华为公司在其全球化进程中逐步构建和完善的一套高效产品开发管理体系。这一流程不仅帮助华为在技术创新和产品交付上实现了质的飞跃,还为其在全球市场中赢得了显著的竞争优势。IPD的核心在于通过跨部门协作、阶段性评审和市场需求驱动,确保...
华为IPD   0  
  华为作为全球领先的通信技术解决方案提供商,其成功的背后离不开一套成熟的管理体系——集成产品开发(IPD)。IPD不仅是一种产品开发流程,更是一种系统化的管理思想,它通过跨职能团队的协作、阶段评审机制和市场需求驱动的开发模式,帮助华为在全球市场中脱颖而出。从最初的国内市场到如今的全球化布局,华为的IPD体系在多个领域展现...
IPD管理流程   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用