为什么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 。 第一个从setinheritance它的__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的比较原因, KeysViewinheritanceSetValuesView不inheritance:

 class KeysView(MappingView, Set): class ValuesView(MappingView): 
  • ValuesView及其父项中跟踪__eq__

    MappingView ==> Sized ==> ABCMeta ==> type ==> object

    __eq__只在object实现,不能被覆盖。

  • 另一方面, KeysView直接从Setinheritance__eq__

不幸的是,目前的两个答案都没有解决这个问题,而是关注如何做到这一点。 那个邮件列表的讨论太棒了,所以我总结一下:

对于odict.keys / dict.keysodict.items / dict.items

  • odict.keysdict.keys子类 )支持比较,因为它符合collections.abc.Set (它是一个集合对象)。 这是可能的,因为词典中的keys (有或没有)保证是唯一和可sorting的。
  • odict.itemsodict.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' 

    对于这两个视图keysitems ,比较使用一个简单的叫做all_contained_in (很可读)的函数,它使用对象__contain__方法检查相关视图中元素的成员资格。

现在,关于odict.values / odict.values

  • 正如所注意到的, odict.valuesodict.values [shocker]的子类 ) 并不像集合对象那样比较。 这是因为valuesviewvalues不能表示为一个集合,原因有两方面:

    1. 最重要的是,该视图可能包含无法删除的重复项。
    2. 该视图可能包含不可散列的对象(它本身不足以将视图视为集合对象)。

正如@ user2357112和@abarnett在邮件列表中的评论中所述, odict.values / odict.values是一个多重集合,是一个集合的泛化,允许多个元素的实例。 试图比较这些并不像比较keysitems由于固有的重复,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]) 

即使与键对应的值不相同,实际上也是相等的? 也许? 也许不会? 这不是直截了当,将导致不可避免的混乱。

要做的一点是,将这些与keysitems进行比较并不是微不足道的, 在邮件列表中用@abarnett的另一个评论来总结:

如果你认为我们可以定义多层应该做什么,尽pipe没有一个标准的multisettypes或为他们的ABC,并将其应用于值的意见,下一个问题是如何做到这一点比二次时间不可哈希值。 (而且你也不能假定在这里订购。)将价值观视为30秒,然后用你直觉上想要的答案回来,而不是在20毫秒内给出错误的答案是一个改进? (无论哪种方式,你都要学习同样的教训:不要比较价值观,我宁愿在20毫米的时间里学习)。