【问题标题】:Why are the values of an OrderedDict not equal?为什么 OrderedDict 的值不相等?
【发布时间】:2016-03-22 15:04:05
【问题描述】:

使用 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.5 对此进行了测试。


根据这个问题,我在 Python-Ideas 邮件列表上发布了更多详细信息:

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

【问题讨论】:

  • 在 Python 2.7.6 上运行良好
  • dict.values 返回一个ValuesView

标签: python python-3.x dictionary ordereddictionary


【解决方案1】:

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

>>> d1.values()
ValuesView(OrderedDict([('foo', 'bar')]))

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

>>> list(d1.values()) == list(d2.values())
True

调查为什么它适用于比较键,在 CPython 的 _collections_abc.py 中,KeysView 继承自 SetValuesView 没有:

class KeysView(MappingView, Set):

class ValuesView(MappingView):
  • ValuesView 中跟踪__eq__ 及其父母:

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

    __eq__ 仅在 object 中实现,不会被覆盖。

  • 另一方面,KeysView 直接从Set 继承__eq__

【讨论】:

  • values() 在 python 3 上返回一个迭代器,在 python 2.7 上返回一个列表
  • @bgusach 它不是迭代器,但它是可迭代的。见view objects
  • @PeterWood,我认为values 相当于itervalues,但你是对的,它们不是。
【解决方案2】:

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

【讨论】:

  • 对我来说似乎是个缺陷。您是否知道不覆盖 __eq__ 的决定是故意的还是仅仅是疏忽?
  • @RobKennedy 不是最简单的想法 - 但 Python 是免费软件,所以没有什么能阻止你提交补丁;)
  • 我想不覆盖object.__eq__ 的决定是有道理的,因为您所比较的内容模棱两可。如果两个ValueViews 以任何顺序包含相同的值,它们是否应该相等?这是否应该独立于密钥?根据您的需要,比较 .items() 或将值放在列表或集合中并进行比较会更清楚。
  • @ajcr 我没有看到OrderedDict 有任何歧义,有一种明显的方法可以做到这一点:当且仅当我们以相同的顺序具有相同的值时相等,而这应该独立于键。
  • @wim:即使这对您来说似乎是一种显而易见的方式,但为OrderedDict 做出决定会给dict 施加压力,使其__eq__ 为其values 和@ 987654338@ 真的做不到。 dict 的 ValuesView 是一组不必要的哈希对象,并且没有有效的方法来比较这样的多组。此外,对顺序敏感的values 比较与existing order-insensitive OrderedDict.keys comparison 不太匹配,并且使keys 比较顺序敏感可能会破坏事情。
【解决方案3】:

不幸的是,当前的两个答案都没有说明为什么会这样,而是专注于如何做到这一点。邮件列表讨论很棒,所以我总结一下:

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

  • odict.keys (subclass of dict.keys) 支持比较,因为它符合collections.abc.Set(它是一个类似集合的对象)。这是可能的,因为字典中的keys 保证是唯一的和可散列的。
  • odict.items (subclass of dict.items) 也支持比较,原因与.keys 相同。 itemsview 被允许这样做,因为如果 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'
    

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

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

  • 如前所述,odict.values (subclass of dict.values [shocker]) 比较像一个类似集合的对象。这是因为valuesviewvalues 不能表示为一个集合,原因有两个:

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

正如@user2357112@abarnett 在邮件列表中的评论中所述,odict.values/dict.values 是一个多重集,是集合的泛化,它允许其元素的多个实例。 尝试比较这些并不像比较keysitems 那样简单,因为固有的重复、排序以及您可能需要考虑与这些值对应的键的事实。 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 对the mailing list 的另一条评论:

如果您认为我们可以定义多重集应该做什么,尽管没有标准的多重集类型或 ABC,并将其应用于值视图,那么下一个问题是如何在比二次时间更好的时间内做到这一点不可散列的值。 (而且您也不能假设在这里订购。)将值视图挂起 30 秒然后返回您直观想要的答案而不是在 20 毫秒内给出错误答案是一种改进吗? (无论哪种方式,你都会学到同样的教训:不要比较价值观点。我宁愿在 20 毫秒内学习。)

【讨论】:

    猜你喜欢
    • 2023-04-05
    • 2018-08-15
    • 1970-01-01
    • 2010-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-14
    相关资源
    最近更新 更多