Nashy ndarray hashability - PullRequest
       1

Nashy ndarray hashability

12 голосов
/ 20 марта 2012

У меня есть некоторые проблемы с пониманием того, как управляется хэшируемость 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

РЕДАКТИРОВАТЬ2: И после комментария 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 ]

8 голосов
/ 20 марта 2012

Я получаю одинаковые результаты в Python 2.6.6 и numpy 1.3.0.Согласно глоссарию Python , объект должен быть хешируемым, если определено __hash__ (а не None) и определено либо __eq__, либо __cmp__.ndarray.__eq__ и ndarray.__hash__ оба определены и возвращают что-то осмысленное, поэтому я не понимаю, почему hash должен потерпеть неудачу.После быстрого поиска в Google, я нашел это сообщение в списке рассылки python.scientific.devel , в котором говорится, что массивы никогда не предназначались для хэширования - поэтому, почему определен ndarray.__hash__, я понятия не имею.Обратите внимание, что isinstance(nparray, collections.Hashable) возвращает True.

РЕДАКТИРОВАТЬ: Обратите внимание, что nparray.__hash__() возвращает то же, что и id(nparray), так что это только реализация по умолчанию.Возможно, было трудно или невозможно удалить реализацию __hash__ в более ранних версиях python (техника __hash__ = None была, по-видимому, введена в 2.6), поэтому они использовали какую-то магию C API, чтобы достичь этого таким образом, чтобыt распространяется на подклассы, и не остановит вас от явного вызова ndarray.__hash__?

В Python 3.2.2 и текущей версии 2.0.0 все отличается от репо.Метод __cmp__ больше не существует, поэтому для его переносимости теперь требуются __hash__ и __eq__ (см. Глоссарий Python 3 ).В этой версии numpy определено ndarray.__hash__, но оно просто None, поэтому не может быть вызвано.hash(nparray) терпит неудачу и isinstance(nparray, collections.Hashable) возвращает False, как и ожидалось.hash(vector) также не работает.

2 голосов
/ 21 марта 2012

Это не четкий ответ, но вот несколько путей, чтобы понять, как это происходит.

Я ссылаюсь здесь на кодовый код выпуска 1.6.1.

В соответствии с реализацией объекта 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 изменен.

Ex: 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...