我在理解如何管理numpy对象的可用性方面遇到了一些问题。
>>> import numpy as np
>>> class Vector(np.ndarray):
... pass
>>> nparray = np.array([0.])
>>> vector = Vector(shape=(1,), buffer=nparray)
>>> ndarray = np.ndarray(shape=(1,), buffer=nparray)
>>> nparray
array([ 0.])
>>> ndarray
array([ 0.])
>>> vector
Vector([ 0.])
>>> '__hash__' in dir(nparray)
True
>>> '__hash__' in dir(ndarray)
True
>>> '__hash__' in dir(vector)
True
>>> hash(nparray)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'numpy.ndarray'
>>> hash(ndarray)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'numpy.ndarray'
>>> hash(vector)
-9223372036586049780
>>> nparray.__hash__()
269709177
>>> ndarray.__hash__()
269702147
>>> vector.__hash__()
-9223372036586049780
>>> id(nparray)
4315346832
>>> id(ndarray)
4315234352
>>> id(vector)
4299616456
>>> nparray.__hash__() == id(nparray)
False
>>> ndarray.__hash__() == id(ndarray)
False
>>> vector.__hash__() == id(vector)
False
>>> hash(vector) == vector.__hash__()
True
怎么来
__hash__
方法,但不是可以使用numpy.ndarray
的类定义__hash__
而 可以使用?我错过了什么吗?
我正在使用Python 2.7.1和numpy 1.6.1
感谢您的帮助!
编辑:添加了对象id
s
EDIT2:
继deinonychusaur评论并试图弄清楚是否基于内容进行散列之后,我使用了numpy.nparray.dtype
并且发现了一些我觉得很奇怪的事情:
>>> [Vector(shape=(1,), buffer=np.array([1], dtype=mytype), dtype=mytype) for mytype in ('float', 'int', 'float128')]
[Vector([ 1.]), Vector([1]), Vector([ 1.0], dtype=float128)]
>>> [id(Vector(shape=(1,), buffer=np.array([1], dtype=mytype), dtype=mytype)) for mytype in ('float', 'int', 'float128')]
[4317742576, 4317742576, 4317742576]
>>> [hash(Vector(shape=(1,), buffer=np.array([1], dtype=mytype), dtype=mytype)) for mytype in ('float', 'int', 'float128')]
[269858911, 269858911, 269858911]
我很困惑...... numpy中是否有一些(类型独立)缓存机制?
答案 0 :(得分:8)
我在Python 2.6.6和numpy 1.3.0中得到了相同的结果。根据{{3}},如果定义了__hash__
(并且不是None
),则对象应该是可清除的,并且定义了__eq__
或__cmp__
。 ndarray.__eq__
和ndarray.__hash__
都已定义并返回有意义的内容,因此我不明白为什么hash
会失败。快速谷歌之后,我找到了the Python glossary,其中声明数组从来没有打算过去 - 所以为什么定义ndarray.__hash__
,我不知道。请注意,isinstance(nparray, collections.Hashable)
会返回True
。
编辑:请注意nparray.__hash__()
返回与id(nparray)
相同的内容,因此这只是默认实现。也许很难或不可能在早期版本的python中删除__hash__
的实现(__hash__ = None
技术显然是在2.6中引入的),所以他们使用了某种C API魔法来实现这一点。不会传播到子类的方式,并且不会阻止你明确地调用ndarray.__hash__
?
Python 3.2.2和repo当前的numpy 2.0.0有所不同。 __cmp__
方法已不再存在,因此可持续性现在需要__hash__
和__eq__
(请参阅this post on the python.scientific.devel mailing list)。在这个numpy版本中,ndarray.__hash__
已定义,但它只是None
,因此无法调用。 hash(nparray)
失败,isinstance(nparray, collections.Hashable)
按预期返回False
。 hash(vector)
也失败了。
答案 1 :(得分:2)
这不是一个明确的答案,但这里有一些跟踪来理解这种行为。
我在这里指的是1.6.1版本的numpy代码。
根据numpy.ndarray
对象实现(查看,numpy/core/src/multiarray/arrayobject.c
),hash
方法设置为NULL
。
NPY_NO_EXPORT PyTypeObject PyArray_Type = {
#if defined(NPY_PY3K)
PyVarObject_HEAD_INIT(NULL, 0)
#else
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
#endif
"numpy.ndarray", /* tp_name */
sizeof(PyArrayObject), /* tp_basicsize */
&array_as_mapping, /* tp_as_mapping */
(hashfunc)0, /* tp_hash */
此tp_hash
属性似乎在numpy/core/src/multiarray/multiarraymodule.c
中被覆盖。查看DUAL_INHERIT
属性被修改的DUAL_INHERIT2
,initmultiarray
和tp_hash
函数。
例: PyArrayDescr_Type.tp_hash = PyArray_DescrHash
根据hashdescr.c
,哈希实现如下:
* How does this work ? The hash is computed from a list which contains all the
* information specific to a type. The hard work is to build the list
* (_array_descr_walk). The list is built as follows:
* * If the dtype is builtin (no fields, no subarray), then the list
* contains 6 items which uniquely define one dtype (_array_descr_builtin)
* * If the dtype is a compound array, one walk on each field. For each
* field, we append title, names, offset to the final list used for
* hashing, and then append the list recursively built for each
* corresponding dtype (_array_descr_walk_fields)
* * If the dtype is a subarray, one adds the shape tuple to the list, and
* then append the list recursively built for each corresponding type
* (_array_descr_walk_subarray)