Python OrderedDict 迭代

31

为什么我的Python OrderedDict会被“无序”初始化?

这里的解决方案比解释要简单。有些事情我还是不明白,也许解释会对其他人和我有所帮助。

>>> from collections import OrderedDict

>>> spam = OrderedDict(s = (1, 2), p = (3, 4), a = (5, 6), m = (7, 8))

>>> spam
OrderedDict([('a', (5, 6)), ('p', (3, 4)), ('s', (1, 2)), ('m', (7, 8))])

>>> for key in spam.keys():
...    print key    
...
#  this is 'ordered' but not the order I wanted....
a
p
s
m

# I was expecting (and wanting):
s
p
a
m

11
"kwargs" 不是有序的。 - georg
可能是将dict转换为OrderedDict的重复问题。 - Mr_and_Mrs_D
3个回答

38

来自文档:

OrderedDict的构造函数和update()方法都接受关键字参数,但是它们的顺序会丢失,因为Python的函数调用语义使用一个普通的无序字典来传递关键字参数。

所以初始化会丢失顺序,因为它基本上是用**kwargs调用构造函数。

编辑: 就解决方案而言(不仅仅是解释)—正如问题提出者在评论中指出的,传入一个元组列表将起作用:

>>> from collections import OrderedDict
>>> spam = OrderedDict([('s',(1,2)),('p',(3,4)),('a',(5,6)),('m',(7,8))])
>>> for key in spam:
...     print(key)
...
s
p
a
m
>>> for key in spam.keys():
...     print(key)
...
s
p
a
m

这是因为它仅接收到一个参数,即列表。


5
所以这就是为什么这个方法可行:OrderedDict([('s',(1,2)),('p',(3,4)),('a',(5,6)),('m',(7,8))]在这种情况下,构造函数接收一个单一参数,即一个[列表]。 - neil.millikin
这个问题可以用Python 3.6解决,根据文档 - 自从3.6版本接受PEP 468后,有序字典(OrderedDict)构造函数和它的update()方法传递关键字参数时保留了顺序。 - gpk27
@chris-krycho,这个不在原位的顺序稍后会被保留,对吧? - gpk27

17

@Chris Krycho给出了一个很好的关于事物失败原因的解释。

如果你查看OrderedDict的repr(),你会得到一个提示,如何从一开始就传达顺序: 你需要使用一个(key, value)对列表来保留由列表给出的键的顺序。

这是我早些时候做的一个例子:

>>> from collections import OrderedDict
>>> spamher = OrderedDict(s=6, p=5, a=4, m=3, h=2, e=1, r=0)
>>> spamher
OrderedDict([('h', 2), ('m', 3), ('r', 0), ('s', 6), ('p', 5), ('a', 4), ('e', 1)])
>>> 
>>> list(spamher.keys())
['h', 'm', 'r', 's', 'p', 'a', 'e']
>>> 
>>> spamher = OrderedDict([('s', 6), ('p', 5), ('a', 4), ('m', 3), ('h', 2), ('e', 1), ('r', 0)])
>>> list(spamher.keys())
['s', 'p', 'a', 'm', 'h', 'e', 'r']
>>> 

偶然地,在 Python v3.3.0 中,你原本的 spam 例子一开始就保持了它们的原始顺序。我改成 spamher 来解决这个问题。


4
作为其他 答案提到的,试图将字典传递给OrderedDict或使用关键字参数不会保留顺序。虽然传递元组有点丑陋,但这是Python,它应该很美。
您可以滥用类上的__getitem__,以便具有类似于字典的语法来创建OrderedDict“文字”:
from collections import OrderedDict
class OD(object):
    """This class provides a nice way to create OrderedDict "literals"."""
    def __getitem__(self, slices):
        if not isinstance(slices, tuple):
            slices = slices,
        return OrderedDict((slice.start, slice.stop) for slice in slices)
# Create a single instance; we don't ever need to refer to the class.
OD = OD()

现在,您可以使用类似字典的语法来创建有序字典:
spam = OD['s': (1, 2), 
          'p': (3, 4), 
          'a': (5, 6), 
          'm': (7, 8)]
assert(''.join(spam.keys()) == 'spam')

这段代码之所以可行,是因为在方括号内,Python创建了slice字面量,如果你稍微眯起眼睛看的话,它们看起来像字典语法。 OD类可以受益于错误检查,但这展示了它是如何工作的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接