gpt4 book ai didi

python - 为什么(某些)dict View 可以散列?

转载 作者:太空狗 更新时间:2023-10-30 03:01:27 26 4
gpt4 key购买 nike

在 python 3 中,keys()values()items() 方法提供了 dynamic views他们各自的元素。这些已向后移植到 python 2.7,并可作为 viewkeysviewvaluesviewitems 使用。我在这里可以互换地指代它们。

有什么合理的解释吗:

#!/usr/bin/python3.4
In [1]: hash({}.keys())
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-1-3727b260127e> in <module>()
----> 1 hash({}.keys())

TypeError: unhashable type: 'dict_keys'

In [2]: hash({}.items())
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-2-decac720f012> in <module>()
----> 1 hash({}.items())

TypeError: unhashable type: 'dict_items'

In [3]: hash({}.values())
Out[3]: -9223363248553358775

我发现这相当令人惊讶。


python 文档 glossary on "hashable"说:

An object is hashable if it has a hash value which never changes during its lifetime (it needs a __hash__() method), and can be compared to other objects (it needs an __eq__() method). Hashable objects which compare equal must have the same hash value.

好的,第一部分实际上已经结束了; dict_values 对象的哈希值似乎不会在其生命周期内发生变化 - 尽管其基础值肯定会发生变化。

In [11]: d = {}

In [12]: vals = d.values()

In [13]: vals.__hash__()
Out[13]: -9223363248553358718

In [14]: d['a'] = 'b'

In [15]: vals
Out[15]: dict_values(['b'])

In [16]: vals.__hash__()
Out[16]: -9223363248553358718

但是关于 __eq__() 的部分......好吧,实际上它没有其中之一。

In [17]: {'a':'a'}.values().__eq__('something else')
Out[17]: NotImplemented

所以...是的。有人能理解这一点吗?在三种 viewfoo 方法中,只有 dict_values 对象是可散列的,这种不对称性是否有原因?

最佳答案

我相信这是因为 viewitemsviewkeys提供自定义丰富的比较功能,但是viewvalues才不是。 Here是每个 View 类型的定义:

PyTypeObject PyDictKeys_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"dict_keys", /* tp_name */
sizeof(dictviewobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)dictview_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)dictview_repr, /* tp_repr */
&dictviews_as_number, /* tp_as_number */
&dictkeys_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
(traverseproc)dictview_traverse, /* tp_traverse */
0, /* tp_clear */
dictview_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)dictkeys_iter, /* tp_iter */
0, /* tp_iternext */
dictkeys_methods, /* tp_methods */
0,
};

PyTypeObject PyDictItems_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"dict_items", /* tp_name */
sizeof(dictviewobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)dictview_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)dictview_repr, /* tp_repr */
&dictviews_as_number, /* tp_as_number */
&dictitems_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
(traverseproc)dictview_traverse, /* tp_traverse */
0, /* tp_clear */
dictview_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)dictitems_iter, /* tp_iter */
0, /* tp_iternext */
dictitems_methods, /* tp_methods */
0,
};

PyTypeObject PyDictValues_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"dict_values", /* tp_name */
sizeof(dictviewobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)dictview_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)dictview_repr, /* tp_repr */
0, /* tp_as_number */
&dictvalues_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
(traverseproc)dictview_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)dictvalues_iter, /* tp_iter */
0, /* tp_iternext */
dictvalues_methods, /* tp_methods */
0,
};

注意 tp_richcompare定义为 dictview_richcompare对于 itemskeys , 但不是 values .现在,documentation for __hash__ 是这样说的:

A class that overrides __eq__() and does not define __hash__() will have its __hash__() implicitly set to None.

...

If a class that overrides __eq__() needs to retain the implementation of __hash__() from a parent class, the interpreter must be told this explicitly by setting __hash__ = <ParentClass>.__hash__.

If a class that does not override __eq__() wishes to suppress hash support, it should include __hash__ = None in the class definition.`

所以,因为 items/keys正在覆盖 __eq__() (通过提供 tp_richcompare 函数),他们需要显式定义 __hash__等于 parent 的保留实现。因为values不会覆盖 __eq__() , 它继承了 __hash__来自 object ,因为 tp_hashtp_richcompare get inherited from the parent if they're both NULL :

This field is inherited by subtypes together with tp_richcompare: a subtype inherits both of tp_richcompare and tp_hash, when the subtype’s tp_richcompare and tp_hash are both NULL.

事实上 dict_values 的执行不阻止这种自动继承可能会被视为错误。

关于python - 为什么(某些)dict View 可以散列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25293912/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com