为什么OrderedDict的值不相等?

Ale*_*dre 50 python dictionary ordereddictionary python-3.x

使用Python 3:

>>> from collections import OrderedDict
>>> d1 = OrderedDict([('foo', 'bar')])
>>> d2 = OrderedDict([('foo', 'bar')])
Run Code Online (Sandbox Code Playgroud)

我想检查是否平等:

>>> d1 == d2
True
>>> d1.keys() == d2.keys()
True
Run Code Online (Sandbox Code Playgroud)

但:

>>> d1.values() == d2.values()
False
Run Code Online (Sandbox Code Playgroud)

你知道为什么价值观不平等吗?

我用Python 3.4和3.5进行了测试.


在这个问题之后,我发布了Python-Ideas邮件列表以获得更多详细信息:

https://mail.python.org/pipermail/python-ideas/2015-December/037472.html

bru*_*ers 41

在Python 3,dict.keys()dict.values()退回特殊迭代类-分别为collections.abc.KeysViewcollections.abc.ValuesView.第一个继承它的__eq__方法set,第二个使用默认object.__eq__测试对象标识.

  • 虽然这可能确实是答案,但这是一个非常不满意的答案...... (9认同)
  • 我想不要覆盖`object .__ eq__`的决定是有道理的,因为它与你所比较的模糊不清.如果两个`ValueView在任何顺序中包含相同的值,它们是否相等?它应该独立于键吗?根据你想要的东西,比较`.items()`或者将值放在列表或集合中并比较它们会更清楚. (6认同)
  • 对我来说似乎是一个缺陷.你是否碰巧知道是否有意改写`__eq__`是故意还是只是疏忽? (4认同)
  • @RobKennedy不是最简单的想法 - 但Python是免费软件,所以没有什么能阻止你提交补丁;) (3认同)

Big*_*her 20

在python3,d1.values()并且d2.values()collections.abc.ValuesView对象:

>>> d1.values()
ValuesView(OrderedDict([('foo', 'bar')]))
Run Code Online (Sandbox Code Playgroud)

不要将它们作为对象进行比较,将它们转换为列表然后进行比较:

>>> list(d1.values()) == list(d2.values())
True
Run Code Online (Sandbox Code Playgroud)

调查为什么它用于比较_collections_abc.pyCPython中的键,KeysView是继承SetValuesView不是:

class KeysView(MappingView, Set):

class ValuesView(MappingView):
Run Code Online (Sandbox Code Playgroud)
  • 寻找__eq__in ValuesView及其父母:

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

    __eq__仅在实现中object而不是被覆盖.

  • 另一方面,直接KeysView继承.__eq__Set

  • @bgusach它不是迭代器,但它是可迭代的.请参阅[查看对象](https://docs.python.org/3/library/stdtypes.html#dict-views). (5认同)
  • `values()`返回python 3上的迭代器,以及python 2.7中的列表 (2认同)
  • @PeterWood,我认为`值'相当于`itervalues`,但你是对的,它们不是. (2认同)

Jim*_*ard 5

不幸的是,当前的两个答案都没有解决为什么这样做,而是集中在如何做到这一点上。该邮件列表讨论非常了不起,所以我总结一下:

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

  • odict.keys(的(的子类dict.keys)支持比较),因为它符合collections.abc.Set(是一个类似集合的对象)。这可能是由于以下事实:keys保证字典内部(有序或无序)是唯一且可哈希的。
  • odict.items(的子类dict.items)也出于相同的原因支持比较.keysitemsview允许这样做,因为如果items中的一个(特别是表示该值的第二个元素)不可哈希,则会引发适当的错误,但可以保证唯一性,但是(由于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'
    
    Run Code Online (Sandbox Code Playgroud)

    对于这两个视图keysitems比较使用一个称为all_contained_in(非常易读)的简单函数,该函数使用objects __contain__方法检查所涉及视图中元素的成员资格。

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

  • 如前所述,odict.values([shocker]的子类dict.values)不像集合对象那样进行比较。这是因为valuesa的valuesview不能表示为集合,原因有两个:

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

正如@ user2357112@abarnett在邮件列表中的注释中所述,odict.values/ dict.values是多集,是集的泛化,允许其元素的多个实例。尝试比较它们并不比比较简单,keys或者items由于固有的重复,排序以及您可能需要考虑与这些值相对应的键这一事实。应该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])
Run Code Online (Sandbox Code Playgroud)

即使对应于键的值不一样,实际上是相等的吗?也许?也许不吧?这两种方法都不是直接的,并且会导致不可避免的混乱。

但是要指出的是,将它们与keys和进行比较并不是很items容易的,总之,还有@abarnett在邮件列表另一条评论:

如果您正在考虑尽管没有标准多集类型或ABC的定义,但我们仍可以定义多集应执行的操作并将其应用于值视图,那么下一个问题是,对于非散列而言,如何比二次时间做得更好价值观。(并且您也不能假设在这里订购。)将值视图挂起30秒钟,然后返回您直观地想要的答案,而不是在20毫秒内给出错误的答案,这会有所改善吗?(无论哪种方式,您都将学习同一课:不要比较值视图。我宁愿以20毫秒为单位学习。)