【问题标题】:Difference between dictionary and OrderedDict字典和 OrderedDict 的区别
【发布时间】:2016-03-22 04:12:03
【问题描述】:

我正在尝试获取已排序的字典。但是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)

【问题讨论】:

    标签: python python-3.x dictionary ordereddictionary


    【解决方案1】:

    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 不会为您订购元素,它保留您给它的顺序。

    如果你想对字典进行“排序”,你可能需要

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

    【讨论】:

    • 要将dict 转换为(最初)具有排序顺序的键的OrderedDict,您可以:od = OrderedDict(sorted(d.items()))
    • 请注意,字典现在默认是插入排序的。
    • 嗯,这种行为在更高的 python 版本中会改变吗?在 Python 3.7.8 imgur.com/OJTKxkX 中似乎保持了顺序
    • @RakshitKothari 是的!请参阅另一个答案 - 在 Python 3.7+ 中,dict 现在的行为与 OrderedDict 相同。
    【解决方案2】:

    有序字典就像普通字典一样,但它们会记住插入项目的顺序。遍历有序字典时,项按其键首次添加的顺序返回。

    所以它只按添加到字典中的顺序排序

    您可以按如下键构建 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

    【讨论】:

      【解决方案3】:

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

      • 您可以将其用作简单的dict 对象,因为它支持与其他Mapping 对象(如collections.counter)进行相等性测试。

      • OrderedDict 保留了 Brian 解释的插入顺序。除此之外,它还有一个方法popitem,它以LIFO 顺序返回(键,值)对。因此,您也可以将其用作映射的“堆栈”。

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

      【讨论】:

      • 它在速度和内存的某些方面确实有代价。
      【解决方案4】:

      截至Python 3.7,对内置dict 的新改进是:

      dict 对象的插入顺序保存特性已被宣布为 Python 语言规范的官方部分。

      这意味着不再需要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 没有内置的功能,但这里显示了解决方法:

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

      【讨论】:

        【解决方案5】:

        CPython 3.6 和所有其他从 Python 3.7 开始的 Python 实现开始,内置的 dict 是有序的 - 您按照您的顺序取出项目插入它们。这使得dictOrderedDict 实际上是相同的。

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

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

        然后有一些小的实际差异:

        • dict.popitem() 不接受任何参数,而 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 旨在擅长重新排序操作。 空间效率、迭代速度和更新性能 操作是次要的。
        • 从算法上讲,OrderedDict 可以处理频繁的重新排序 操作优于dict。这使它适合跟踪 最近的访问(例如LRU cache)。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-09-21
          • 2011-05-16
          • 2011-06-13
          • 1970-01-01
          • 2011-10-01
          • 2020-12-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多