dict
自Python 3.6以来保持插入顺序(参见此)。
OrderedDict
就是为此目的而开发的(Python 3.6之前)。
从Python 3.6开始,dict
或OrderedDict
的键顺序是否总是相同的?
我想知道我是否可以在我的代码中做到这一点,并始终具有相同的行为(除了相等,以及OrderedDict
中的一些扩展方法),但更有效:
if sys.version_info[:2] >= (3, 6):
OrderedDict = dict
else:
from collections import OrderedDict
或者换句话说,对于Python>=3.6,是否有理由使用OrderedDict
?
对于迭代,OrderedDict
和dict
都是插入顺序¹。如果迭代顺序是唯一的决定点,特别是在不需要重新排序的情况下,实际上没有理由使用OrderedDict
。
显然,如果需要比较顺序,OrderedDict
和dict
是不可互换的。
或者换句话说,对于Python>=3.6,是否有理由使用
OrderedDict
?
这些天OrderedDict
对dict
就像deque
对list
,基本上。OrderedDict
/deque
基于链表²,而dict
/list
基于数组。前者有更好的pop/move/FIFO语义,因为项目可以从开始/中间删除而不移动其他项目。
由于数组通常非常适合缓存,因此链表的优势只适用于非常大的容器。此外,OrderedDict
(与deque
不同)不保证其链表语义,因此它的优势可能不具有可移植性。如果需要许多pop/move/FIFO操作,则应该主要使用OrderedDict
,并且基准测试可以比较dict
与OrderedDict
在实践中的性能。
¹这适用于所有当前支持的符合Python语言规范的实现,即自Python 3.6以来的CPython和PyPy。
²OrderedDict
在CPython中仍然保留O(1)键访问。这是通过也有一个"regular"查找表,使用链表对项之间的顺序和查找表对直接项访问。很复杂。
我意识到相等的不同行为(__eq__
)实际上可能是一个主要问题,为什么这样的代码片段可能不好。
然而,你可能仍然可以这样做:
if sys.version_info[:2] >= (3, 6):
OrderedDict = dict
else:
from collections import OrderedDict as _OrderedDict
class OrderedDict(_OrderedDict):
__eq__ = dict.__eq__
__ne__ = dict.__ne__
不同的是,与原来的collections.OrderedDict
相比,{1:1,2:2}
与{2:2,1:1}
不一样,但对于dict
和我在这个例子中覆盖的OrderedDict
,它是一样的。
一些行为保持不变,但OrderedDict是可逆的,dict不是:
from collections import OrderedDict
d = { "a" : 1, "c" : 2}
od = OrderedDict(d.items())
print(list(reversed(od)))
print(list(reversed(d)))
['c', 'a']
Traceback (most recent call last):
File "path/to/file", line 8, in <module>
print(list(reversed(d)))
TypeError: 'dict' object is not reversible
来自文档:
除了常用的映射方法,有序字典也使用reversed()支持反向迭代。