30

为什么我的 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
4

3 回答 3

37

文档

OrderedDict 构造函数和update()方法都接受关键字参数,但它们的顺序丢失了,因为 Python 的函数调用语义使用常规无序字典传入关键字参数。

所以初始化失去了顺序,因为它基本上是用**kwargs.

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

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

这是因为它只得到一个参数,一个列表。

于 2013-05-14T22:00:11.543 回答
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来解决这个问题)。

于 2013-05-14T23:45:59.017 回答
4

正如其他 答案所提到的,尝试将 dict 传递给 OrderedDict 或使用关键字参数不会保留顺序。不过,传入元组有点难看,这就是 Python。它应该很漂亮。

您可以在类上使用ab以使用__getitem__类似 dict 的语法来创建 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()

现在您可以使用类似 dict 的语法来创建 OrderedDict:

spam = OD['s': (1, 2), 
          'p': (3, 4), 
          'a': (5, 6), 
          'm': (7, 8)]
assert(''.join(spam.keys()) == 'spam')

这是有效的,因为在方括号内,Python 创建了切片文字,如果你稍微眯一下,它恰好看起来像 dict 语法。

该类OD可以从错误检查中受益,但这展示了它是如何工作的。

于 2015-12-16T19:16:05.320 回答