如何保持键/值与声明的顺序相同?


320

我有一本按照特定顺序声明的字典,并希望一直保持该顺序。键/值实际上不能根据它们的值按顺序保留,我只希望按声明的顺序保留。

因此,如果我有字典:

d = {'ac': 33, 'gw': 20, 'ap': 102, 'za': 321, 'bs': 10}

如果我查看它或遍历它,则不是按此顺序进行的,有什么方法可以确保Python保持我声明键/值的显式顺序?

Answers:


229

从Python 3.6开始,标准dict类型默认会保留插入顺序。

定义

d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}

将产生字典,其中的键按源代码中列出的顺序排列。

这是通过将一个带有整数的简单数组用于稀疏哈希表来实现的,其中这些整数索引到另一个存储键值对(加上计算得出的哈希值)的数组中。后面的数组恰好按插入顺序存储项目,实际上,整个组合使用的内存少于Python 3.5及之前版本中使用的实现。有关详细信息,请参阅Raymond Hettinger原始想法帖子

在3.6中,这仍被视为实现细节;请参阅Python 3.6文档的新增功能

此新实现的顺序保留方面被认为是实现细节,因此不应依赖(将来可能会更改,但是希望在更改语言规范之前,先在几个版本中使用该新dict实现该语言,为所有当前和将来的Python实现强制要求保留顺序的语义;这还有助于保留与仍旧有效的随机迭代顺序的旧版本语言(例如Python 3.5)的向后兼容性。

Python 3.7将此实现细节提升为语言规范,因此现在必须dict保留与该版本或更高版本兼容的所有Python实现中的顺序。参见BDFL的声明

在某些情况下,您可能仍想使用collections.OrderedDict()该类,因为它在标准dict类型的基础上提供了一些附加功能。例如是可逆的(这扩展到视图对象),并支持重新排序(通过move_to_end()方法)。


不幸的是,这与词典创建无关。不能保证在代码示例中指定的字典都保持相同的顺序。这意味着您需要创建一个空的dict,并在需要控制顺序时手动插入每个元素。
naught101 '19

8
@ naught101:不,这确实适用于字典创建。在Python 3.7及更高版本中,保证使用问题中所示的dict显示屏按顺序列出这些键。编写的键值对从左到右插入,因为语言规范保证如果给出了逗号分隔的键/基准对序列,则从左到右对其进行求值以定义字典的条目。该dict()文档甚至包括一个示例。
马丁·彼得斯

175
from collections import OrderedDict
OrderedDict((word, True) for word in words)

包含

OrderedDict([('He', True), ('will', True), ('be', True), ('the', True), ('winner', True)])

如果值是True(或任何其他不可变对象),则还可以使用:

OrderedDict.fromkeys(words, True)

2
当然,值得注意的是,“不可变”部分并不是Python将要执行的一成不变的规则-它的“唯一”是个好主意。
lvc 2012年

11
请注意,解决方案(如:OrderedDict(FUTURE=[], TODAY=[], PAST=[])无效)在提OrderedDict([('FUTURE', []), ('TODAY', []), ('PAST', [])])及时会失效:将保持秩序。
andilabs 2014年

2
@andi我遇到另一个问题,当使用jsonify时,在生成json数据时,OrderedDict似乎丢失了顺序。是否可以解决此问题?
tyan


5
Python3.7现在默认情况下已将dict排序。mail.python.org/pipermail/python-dev/2017-December/151283.html
sertsedat '18

167

我不会解释理论部分,而是给出一个简单的示例。

>>> from collections import OrderedDict
>>> my_dictionary=OrderedDict()
>>> my_dictionary['foo']=3
>>> my_dictionary['aol']=1
>>> my_dictionary
OrderedDict([('foo', 3), ('aol', 1)])
>>> dict(my_dictionary)
{'foo': 3, 'aol': 1}

16
有没有办法像Dict类型一样批量分配OrderedDict?
tyan

2
OrderedDict确实解决了问题,但是...在此特定示例中,使用标准字典可获得完全相同的结果
Tonechas

2
@Tonechas:我只是用标准字典尝试了这个例子,{'aol': 1, 'foo': 3}所以我觉得这是一个很好的说明性例子。
twasbrillig's

4
每个人都有一个教训:发现(我认为是在2.4版本附近),Python的可预测哈希可能会引起安全漏洞,因此,现在无法保证即使是同一代码的两次不同运行也将在标准中给出相同的顺序字典
holdenweb'9

1
@tyan可以OrderedDict.update()使用包含键值对的可迭代对象进行调用:d1.upate([(key1, val1), (key2, val2)])
Ruud Althuizen

37

请注意,此答案适用于python3.7之前的python版本。CPython 3.6在大多数情况下都将插入顺序作为实现细节来维护。从Python3.7开始,已经声明实现必须维护插入顺序以使其兼容。


python字典是无序的。如果需要有序字典,请尝试collections.OrderedDict

注意,OrderedDict是在python 2.7中引入的标准库。如果您使用的是python的旧版本,则可以在ActiveState上找到有序词典的食谱。


参见上面的@martijn的帖子。从python 3.6开始,dict支持插入顺序。
tpk

13

字典将使用使搜索有效的顺序,您无法更改该顺序,

您可以只使用对象列表(在简单情况下为2元素元组,甚至是一个类),然后将项目附加到末尾。然后,您可以使用线性搜索在其中查找项目。

或者,您可以创建或使用为维护顺序而创建的其他数据结构。


字典将使用使搜索有效的顺序。最后,有人指出了这一点。
scharette

7

我在尝试弄清楚如何使OrderedDict工作时遇到了这篇文章。用于Eclipse的PyDev根本找不到OrderedDict,所以我最终决定根据字典的键值创建一个元组,因为我希望对它们进行排序。当我需要输出列表时,我只需遍历元组的值,然后将元组中迭代的“键”插入字典中,即可按需要的顺序检索值。

例:

test_dict = dict( val1 = "hi", val2 = "bye", val3 = "huh?", val4 = "what....")
test_tuple = ( 'val1', 'val2', 'val3', 'val4')
for key in test_tuple: print(test_dict[key])

这有点麻烦,但是我时间紧迫,这是我想出的解决方法。

注意:其他人建议的列表列表方法对我来说真的没有意义,因为列表是有序的和索引的(并且结构与字典不同)。


很好的解决方案。我将使用它来将json写入文件,总是以相同的顺序进行。
Hrvoje T

6

您实际上无法使用字典来完成所需的工作。您已经d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}创建了词典。我发现一旦创建就无法保持秩序。我所做的是用对象制作了一个json文件:

{"ac":33,"gw":20,"ap":102,"za":321,"bs":10}

我用了:

r = json.load(open('file.json'), object_pairs_hook=OrderedDict)

然后使用:

print json.dumps(r)

核实。


1
那么,为什么不从列表中的OrderedDict开始呢?JSON文件实际上并未在此处添加任何内容。
马丁·彼得斯

是的,列表对于保持秩序更有用,但是答案是关于字典排序的问题。只是让人们知道使用字典的局限性,如果他们出于某种原因需要使用字典,可以给他们一个可能的解决方法。
nealous3

1
但是该部分早在2012
。–马丁·彼得斯

3
from collections import OrderedDict
list1 = ['k1', 'k2']
list2 = ['v1', 'v2']
new_ordered_dict = OrderedDict(zip(list1, list2))
print new_ordered_dict
# OrderedDict([('k1', 'v1'), ('k2', 'v2')])

主要问题不再是字典,而是元组列表
Oleg

2

另一种选择是使用Pandas,dataframe因为它可以保证像dict这样的结构中项目的顺序和索引位置。


1

一般情况下,你可以设计一个类,像一本字典的行为,主要是实现方法__contains____getitem____delitem____setitem__和更多一些。该类可以具有您喜欢的任何行为,例如,对键使用排序的迭代器...


1

如果您希望按特定顺序拥有字典,则还可以创建一个列表列表,其中第一项为键,第二项为值,看起来像此示例

>>> list =[[1,2],[2,3]]
>>> for i in list:
...     print i[0]
...     print i[1]

1
2
2
3

7
那不是“字典”,因为如果不搜索整个集合(花费O(n)时间),就无法通过它们的键来查找项目。
BHSPitMonkey 2014年

1
是的,它不是字典,但是根据情况,它可以为原始海报提供有效的解决方案。
SunSparc 2015年

他没有确切说出我们想要的方式,只是想要订购它们=),因为总是有很多方法可以做一件事情。
pelos

1

开发Django项目时遇到类似的问题。我无法使用OrderedDict,因为我正在运行旧版本的python,因此解决方案是使用Django的SortedDict类:

https://code.djangoproject.com/wiki/SortedDict

例如,

from django.utils.datastructures import SortedDict
d2 = SortedDict()
d2['b'] = 1
d2['a'] = 2
d2['c'] = 3

注意:此答案最初来自2011年。如果您可以访问python 2.7或更高版本,则应该可以访问现在的standard collections.OrderedDict,该线程中的其他示例已经提供了其中的很多示例。


0

您可以做与字典一样的事情。

创建一个列表并清空字典:

dictionary_items = {}
fields = [['Name', 'Himanshu Kanojiya'], ['email id', 'hima@gmail.com']]
l = fields[0][0]
m = fields[0][1]
n = fields[1][0]
q = fields[1][1]
dictionary_items[l] = m
dictionary_items[n] = q
print dictionary_items
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.