在python 3中,keys()
,values()
和items()
方法提供了各自元素的dynamic views。这些被反向移植到python 2.7,并以viewkeys
,viewvalues
和viewitems
的形式提供。我在这里可以互换地提到它们。
对此有任何合理的解释:
#!/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 docs glossary on "hashable"说:
如果对象具有永不更改的哈希值,则该对象是可清除的 在其生命周期中(它需要
__hash__()
方法),并且可以 与其他对象相比(它需要__eq__()
方法)。可哈希 比较相等的对象必须具有相同的哈希值。
好的,第一部分实际检查出来;它并不表示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
个对象是可以清除的吗?
答案 0 :(得分:3)
我认为这是因为viewitems
和viewkeys
提供了自定义丰富的比较功能,但viewvalues
没有。 Here是每种视图类型的定义:
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
的{{1}}被定义为items
,而keys
被定义为values
。现在,documentation for __hash__
说明了这一点:
覆盖
__eq__()
且未定义__hash__()
的类会将其__hash__()
隐式设置为无。...
如果覆盖
__eq__()
的类需要保留实现 来自父类的__hash__()
,必须告诉解释者 明确地设置__hash__ = <ParentClass>.__hash__
。如果未覆盖
__eq__()
的类希望禁止散列 支持,它应该在类定义中包含__hash__ = None
。
因此,由于items
/ keys
覆盖__eq__()
(通过提供tp_richcompare
函数),他们需要明确将__hash__
定义为相等向父母保留其实施。由于values
未覆盖__eq__()
,因此它会从__hash__
继承object
,因为tp_hash
和tp_richcompare
get inherited from the parent if they're both NULL:< / p>
此字段由子类型和tp_richcompare一起继承:a 当子类型继承tp_richcompare和tp_hash时 子类型的tp_richcompare和tp_hash都是NULL。
dict_values
的驱逐不会阻止这种自动继承的事实可能会被视为一个错误。