numpy ndarray可靠性

时间:2012-03-20 11:03:31

标签: python numpy

我在理解如何管理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

怎么来

  • numpy对象定义了__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中是否有一些(类型独立)缓存机制?

2 个答案:

答案 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)按预期返回Falsehash(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_INHERIT2initmultiarraytp_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)