字典和 OrderedDict 的区别

Difference between dictionary and OrderedDict

我正在尝试获取已排序的字典。但是 mydictorddict 之间的项目顺序似乎没有改变。

from collections import OrderedDict

mydict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

orddict = OrderedDict(mydict)

print(mydict, orddict)

# print items in mydict:
print('mydict')
for k, v in mydict.items():
    print(k, v)

print('ordereddict')
# print items in ordered dictionary
for k, v in orddict.items():
    print(k, v)


# print the dictionary keys
# for key in mydict.keys():
#     print(key)


#  print the dictionary values
# for value in mydict.values():
#     print(value)

OrderedDict 保留元素插入的顺序:

>>> od = OrderedDict()
>>> od['c'] = 1
>>> od['b'] = 2
>>> od['a'] = 3
>>> od.items()
[('c', 1), ('b', 2), ('a', 3)]
>>> d = {}
>>> d['c'] = 1
>>> d['b'] = 2
>>> d['a'] = 3
>>> d.items()
[('a', 3), ('c', 1), ('b', 2)]

所以 OrderedDict 不会为您 排序 元素,它 保留 您给它的顺序。

如果你想要"sort"一本字典,你可能想要

>>> sorted(d.items())
[('a', 1), ('b', 2), ('c', 3)]

Ordered dictionaries are just like regular dictionaries but they remember the order that items were inserted. When iterating over an ordered dictionary, the items are returned in the order their keys were first added.

所以它只按照添加到dict中的顺序排序

您可以通过以下方式按键构建OrderedDict订单,

orddict = OrderedDict(sorted(mydict.items(), key = lambda t: t[0]))

或者就像@ShadowRanger 在评论中提到的那样

orddict = OrderedDict(sorted(d.items()))

如果要按值排序,

orddict = OrderedDict(sorted(mydict.items(), key = lambda t: t[1]))

更多信息见8.3.5.1. OrderedDict Examples and Recipes

加上 Brian 的回答,OrderedDict 真的很棒。原因如下:

  • 您可以将它用作简单的 dict 对象,因为它支持与其他 Mapping 对象(例如 collections.counter.

    [=28)进行相等性测试=]
  • OrderedDict 保留了 Brian 解释的插入顺序。除此之外,它还有一个方法 popitem,其中 returns (key,value) 以 LIFO 顺序配对。因此,您也可以将其用作映射 'stack'.

您不仅可以获得 dict 的全部功能,还可以获得一些很酷的技巧。

Python 3.7 起,对 dict built-in 的新改进是:

the insertion-order preservation nature of dict objects has been declared to be an official part of the Python language spec.

这意味着不再需要 OrderedDict。他们几乎是一样的。


需要考虑的一些小细节...

以下是 Python 3.7+ dictOrderedDict 之间的一些比较:

from collections import OrderedDict

d = {'b': 1, 'a': 2}
od = OrderedDict([('b', 1), ('a', 2)])

# they are equal with content and order
assert d == od
assert list(d.items()) == list(od.items())
assert repr(dict(od)) == repr(d)

显然,这两个对象的字符串表示形式存在差异,dict 对象的形式更自然、更紧凑。

str(d)  # {'b': 1, 'a': 2}
str(od) # OrderedDict([('b', 1), ('a', 2)])

至于两者的方法不同,这个问题可以用集合论来回答:

d_set = set(dir(d))
od_set = set(dir(od))
od_set.difference(d_set)
# {'__dict__', '__reversed__', 'move_to_end'}  for Python 3.7
# {'__dict__', 'move_to_end'}  for Python 3.8+

这意味着 OrderedDict 最多有两个 dict 没有 built-in 的特征,但是 work-arounds 显示在这里:

__reversed__ / reversed()

的解决方法

Python 3.8+、which fixed this issue 确实不需要解决方法。 OrderedDict 可以“反转”,它只是反转键(不是整个字典):

reversed(od)        # <odict_iterator at 0x7fc03f119888>
list(reversed(od))  # ['a', 'b']

# with Python 3.7:
reversed(d)                    # TypeError: 'dict' object is not reversible
list(reversed(list(d.keys()))) # ['a', 'b']

# with Python 3.8+:
reversed(d)        # <dict_reversekeyiterator at 0x16caf9d2a90>
list(reversed(d))  # ['a', 'b']

使用 Python 3.7+:

正确反转整个字典
dict(reversed(list(d.items())))  # {'a': 2, 'b': 1}

move_to_end

的解决方法

OrderedDict有一个move_to_end方法,实现起来很简单:

od.move_to_end('b')  # now it is: OrderedDict([('a', 2), ('b', 1)])

d['b'] = d.pop('b')  # now it is: {'a': 2, 'b': 1}

CPython 3.6 开始,所有其他 Python 实现从 Python 3.7 开始,内置的 dict 已排序 - 您按照插入的顺序取出物品。这使得 dictOrderedDict 实际上相同。

The documentation for OrderedDict 列出了剩余的差异。最重要的是

  • OrderedDict 的相等运算检查匹配顺序。

还有一些小的实际差异:

  • dict.popitem() takes no arguments, whereas OrderedDict.popitem(last=True) 接受一个可选的 last= 参数,让您弹出第一项而不是最后一项。
  • OrderedDict 有一个 move_to_end(key, last=True) 方法可以有效地将元素重新定位到末尾或开头。使用 dicts,您可以通过重新插入键将其移动到末尾:mydict['key'] = mydict.pop('key')
  • 直到 Python 3.8,你可以做 reversed(OrderedDict())reversed({}) 会引发 TypeError: 'dict' object is not reversible 错误,因为他们忘记添加 __reversed__ dunder 方法dict 他们下单的时候。现在已修复。

还有一些底层差异可能意味着您可以通过 OrderedDict:

为某些特定用例获得更好的性能
  • 常规 dict 被设计为非常擅长映射 操作。跟踪广告顺序是次要的。
  • OrderedDict 被设计为擅长重新排序操作。 Space效率、迭代速度、更新性能 操作是次要的。
  • 算法上,OrderedDict 可以处理频繁的重新排序 操作优于 dict。这使其适合跟踪 最近访问(例如 LRU cache)。

请参阅此 great talk from 2016 by Raymond Hettinger 了解有关 Python 词典如何实现的详细信息。