如何将包含NumPy数组的元组变为可哈希化的?

9

使numpy数组可哈希的一种方法是将其设置为只读。在我过去的经验中,这种方式行之有效。但是,当我在元组中使用这样的numpy数组时,整个元组不再可哈希,我不理解这个问题。下面是我为说明问题而编写的示例代码:

import numpy as np

npArray = np.ones((1,1))
npArray.flags.writeable = False
print(npArray.flags.writeable)

keySet = (0, npArray)
print(keySet[1].flags.writeable)

myDict = {keySet : 1}

首先我创建了一个简单的numpy数组并将其设置为只读。然后我将它添加到一个元组中,并检查它是否仍然是只读的(是只读的)。

当我想要将元组用作字典中的键时,我会收到错误消息TypeError: unhashable type: 'numpy.ndarray'

以下是我的示例代码输出:

False
False
Traceback (most recent call last):
  File "test.py", line 10, in <module>
    myDict = {keySet : 1}
TypeError: unhashable type: 'numpy.ndarray'

我该怎么做才能使我的元组具有可哈希性,为什么Python会首先显示这种行为?

1
你从哪里得到的想法,认为将“writeable”标志设置为“False”会使数组可哈希?即使在引入元组之前,这也是行不通的。 - user2357112
一个numpy数组没有所需的哈希方法(类似于__hash__)。 - hpaulj
该方法在此处描述:https://dev59.com/9WQn5IYBdhLWcg3wxZYn - Demento
2个回答

14

你声称

使numpy数组可散列的一种方式是将其设置为只读

但实际上并不是这样。将数组设置为只读只会使其变为只读状态,并不能使其可散列,有多个原因。

第一个原因是,将标志位writeable设置为False的数组仍然是可变的。首先,您始终可以再次将writeable = True,并继续对其进行编写,或者执行更多奇特的操作,例如在writeable = False 的情况下重新分配其形状。其次,即使不触摸数组本身,您也可以通过另一个具有writeable = True的视图来改变其数据。

>>> x = numpy.arange(5)
>>> y = x[:]
>>> x.flags.writeable = False
>>> x
array([0, 1, 2, 3, 4])
>>> y[0] = 5
>>> x
array([5, 1, 2, 3, 4])

其次,要使哈希具有意义,对象必须首先是可比较的 - == 必须返回一个布尔值,并且必须是等价关系。NumPy 数组并不能做到这一点。哈希值的目的是快速定位相等的对象,但当您的对象甚至没有内置的相等概念时,提供哈希也没有太大意义。


你用数组无法获得可哈希的元组。你甚至无法获得可哈希的数组。你能做到的最接近的方法是将数组数据的某些其他表示放入元组中。


2
+1,感谢对内部细节的澄清。如果不能使用哈希数组的想法,我将首先将其转换为字符串。 - Demento
能否创建numpy数组的某种包装器,使它们成为可哈希和可比较的对象?这样就不必在字节/字符串等表示法之间来回切换了。 - user76284

9

将numpy数组哈希化的最快方法可能是使用tostring函数。

In [11]: %timeit hash(y.tostring())

你可以定义一个类,而不是使用元组:
class KeySet(object):
    def __init__(self, i, arr):
        self.i = i
        self.arr = arr
    def __hash__(self):
        return hash((self.i, hash(self.arr.tostring())))

现在您可以将其用于字典中:
In [21]: ks = KeySet(0, npArray)

In [22]: myDict = {ks: 1}

In [23]: myDict[ks]
Out[23]: 1

2
这感觉很Pythonic,谢谢快速回答!我还可以使用一个函数包装numpy.fromstring()将其转换回来,这是我以后需要的。 - Demento

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接