为什么OrderedDict的值不相等?
用Python 3:
>>> from collections import OrderedDict >>> d1 = OrderedDict([('foo', 'bar')]) >>> d2 = OrderedDict([('foo', 'bar')])
我想检查平等:
>>> d1 == d2 True >>> d1.keys() == d2.keys() True
但:
>>> d1.values() == d2.values() False
你知道为什么价值不平等吗?
我已经用Python 3.4和3.5testing过了。
在这个问题之后,我在Python-Ideas邮件列表上发布了更多的细节:
https://mail.python.org/pipermail/python-ideas/2015-December/037472.html
在Python 3中, dict.keys()
和dict.values()
返回特殊的可迭代类 – 分别是一个collections.abc.KeysView
和一个collections.abc.ValuesView
。 第一个从set
inheritance它的__eq__
方法,第二个使用默认object.__eq__
,它testing对象标识。
在python3中, d1.values()
和d2.values()
是collections.abc.ValuesView
对象:
>>> d1.values() ValuesView(OrderedDict([('foo', 'bar')]))
不要把它们作为一个对象进行比较,把它们转换成列表,然后比较它们:
>>> list(d1.values()) == list(d2.values()) True
调查CPython的KeysView
中_collections_abc.py
的比较原因, KeysView
inheritanceSet
而ValuesView
不inheritance:
class KeysView(MappingView, Set): class ValuesView(MappingView):
-
在
ValuesView
及其父项中跟踪__eq__
:MappingView ==> Sized ==> ABCMeta ==> type ==> object
。__eq__
只在object
实现,不能被覆盖。 -
另一方面,
KeysView
直接从Set
inheritance__eq__
。
不幸的是,目前的两个答案都没有解决这个问题,而是关注如何做到这一点。 那个邮件列表的讨论太棒了,所以我总结一下:
对于odict.keys
/ dict.keys
和odict.items
/ dict.items
:
-
odict.keys
(dict.keys
子类 )支持比较,因为它符合collections.abc.Set
(它是一个集合对象)。 这是可能的,因为词典中的keys
(有或没有)保证是唯一和可sorting的。 -
odict.items
(odict.items
子类 )也支持与.keys
相同的原因进行比较。itemsview
被允许做到这一点,因为如果其中一个item
(特别是代表值的第二个元素)不可散列,那么它会引发适当的错误,但唯一性是有保证的(尽pipe由于keys
是唯一的):>>> od = OrderedDict({'a': []}) >>> set() & od.items() TypeErrorTraceback (most recent call last) <ipython-input-41-a5ec053d0eda> in <module>() ----> 1 set() & od.items() TypeError: unhashable type: 'list'
对于这两个视图
keys
,items
,比较使用一个简单的叫做all_contained_in
(很可读)的函数,它使用对象__contain__
方法检查相关视图中元素的成员资格。
现在,关于odict.values
/ odict.values
:
-
正如所注意到的,
odict.values
(odict.values
[shocker]的子类 ) 并不像集合对象那样比较。 这是因为valuesview
的values
不能表示为一个集合,原因有两方面:- 最重要的是,该视图可能包含无法删除的重复项。
- 该视图可能包含不可散列的对象(它本身不足以将视图视为集合对象)。
正如@ user2357112和@abarnett在邮件列表中的评论中所述, odict.values
/ odict.values
是一个多重集合,是一个集合的泛化,允许多个元素的实例。 试图比较这些并不像比较keys
或items
由于固有的重复,sorting和事实,你可能需要考虑与这些值相对应的密钥。 应该像这样的dict_values
:
>>> {1:1, 2:1, 3:2}.values() dict_values([1, 1, 2]) >>> {1:1, 2:1, 10:2}.values() dict_values([1, 1, 2])
即使与键对应的值不相同,实际上也是相等的? 也许? 也许不会? 这不是直截了当,将导致不可避免的混乱。
要做的一点是,将这些与keys
和items
进行比较并不是微不足道的, 在邮件列表中用@abarnett的另一个评论来总结:
如果你认为我们可以定义多层应该做什么,尽pipe没有一个标准的multisettypes或为他们的ABC,并将其应用于值的意见,下一个问题是如何做到这一点比二次时间不可哈希值。 (而且你也不能假定在这里订购。)将价值观视为30秒,然后用你直觉上想要的答案回来,而不是在20毫秒内给出错误的答案是一个改进? (无论哪种方式,你都要学习同样的教训:不要比较价值观,我宁愿在20毫米的时间里学习)。