为什么 OrderedDict 的值不相等?
Why are the values of an OrderedDict not equal?
与Python3:
>>> 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
在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
是从 Set
继承的,而 ValuesView
不是:
class KeysView(MappingView, Set):
class ValuesView(MappingView):
在 ValuesView
及其父代中追踪 __eq__
:
MappingView ==> Sized ==> ABCMeta ==> type ==> object
.
__eq__
仅在 object
中实现,未被覆盖。
另一方面,KeysView
直接从Set
继承__eq__
。
在Python3中,dict.keys()
和dict.values()
return特殊可迭代类——分别是一个collections.abc.KeysView
和一个collections.abc.ValuesView
.第一个从 set
继承它的 __eq__
方法,第二个使用默认的 object.__eq__
来测试对象身份。
不幸的是,当前的两个答案都没有说明为什么会这样,而是着重于如何做到这一点。邮件列表的讨论很棒,所以我总结一下:
对于 odict.keys
/dict.keys
和 odict.items
/dict.items
:
odict.keys
(subclass of dict.keys
) 支持比较,因为它符合 collections.abc.Set
(它是一个类似集合的对象)。这是可能的,因为字典中的 keys
(有序或非有序)保证是唯一且可哈希的。
odict.items
(subclass of dict.items
) 也支持比较,原因与 .keys
相同。 itemsview
被允许这样做,因为如果 item
之一(具体来说,表示值的第二个元素)不可哈希,它会引发适当的错误,但可以保证唯一性(由于 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
/dict.values
:
如前所述,odict.values
(subclass of dict.values
[令人震惊]) 不像集合对象那样进行比较。这是因为一个valuesview
的values
不能表示为一个集合,原因有两个:
- 最重要的是,视图可能包含无法删除的重复项。
- 视图可能包含不可散列的对象(它本身不足以不将视图视为类似集合的对象)。
正如 @user2357112 and by @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])
实际上是相等的,即使对应于键的值不一样?可能是?也许不吧?这两种方式都不是直截了当的,并且会导致不可避免的混乱。
不过要指出的是,将它们与 keys
和 items
进行比较并非微不足道,总而言之,@abarnett 在 [=49 上的另一条评论=]:
If you're thinking we could define what multisets should do, despite not having a standard multiset type or an ABC for them, and apply that to values views, the next question is how to do that in better than quadratic time for non-hashable values. (And you can't assume ordering here, either.) Would having a values view hang for 30 seconds and then come back with the answer you intuitively wanted instead of giving the wrong answer in 20 millis be an improvement? (Either way, you're going to learn the same lesson: don't compare values views. I'd rather learn that in 20 millis.)
与Python3:
>>> 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
在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
是从 Set
继承的,而 ValuesView
不是:
class KeysView(MappingView, Set):
class ValuesView(MappingView):
在
ValuesView
及其父代中追踪__eq__
:MappingView ==> Sized ==> ABCMeta ==> type ==> object
.__eq__
仅在object
中实现,未被覆盖。另一方面,
KeysView
直接从Set
继承__eq__
。
在Python3中,dict.keys()
和dict.values()
return特殊可迭代类——分别是一个collections.abc.KeysView
和一个collections.abc.ValuesView
.第一个从 set
继承它的 __eq__
方法,第二个使用默认的 object.__eq__
来测试对象身份。
不幸的是,当前的两个答案都没有说明为什么会这样,而是着重于如何做到这一点。邮件列表的讨论很棒,所以我总结一下:
对于 odict.keys
/dict.keys
和 odict.items
/dict.items
:
odict.keys
(subclass ofdict.keys
) 支持比较,因为它符合collections.abc.Set
(它是一个类似集合的对象)。这是可能的,因为字典中的keys
(有序或非有序)保证是唯一且可哈希的。odict.items
(subclass ofdict.items
) 也支持比较,原因与.keys
相同。itemsview
被允许这样做,因为如果item
之一(具体来说,表示值的第二个元素)不可哈希,它会引发适当的错误,但可以保证唯一性(由于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
/dict.values
:
如前所述,
odict.values
(subclass ofdict.values
[令人震惊]) 不像集合对象那样进行比较。这是因为一个valuesview
的values
不能表示为一个集合,原因有两个:- 最重要的是,视图可能包含无法删除的重复项。
- 视图可能包含不可散列的对象(它本身不足以不将视图视为类似集合的对象)。
正如 @user2357112 and by @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])
实际上是相等的,即使对应于键的值不一样?可能是?也许不吧?这两种方式都不是直截了当的,并且会导致不可避免的混乱。
不过要指出的是,将它们与 keys
和 items
进行比较并非微不足道,总而言之,@abarnett 在 [=49 上的另一条评论=]:
If you're thinking we could define what multisets should do, despite not having a standard multiset type or an ABC for them, and apply that to values views, the next question is how to do that in better than quadratic time for non-hashable values. (And you can't assume ordering here, either.) Would having a values view hang for 30 seconds and then come back with the answer you intuitively wanted instead of giving the wrong answer in 20 millis be an improvement? (Either way, you're going to learn the same lesson: don't compare values views. I'd rather learn that in 20 millis.)