矩阵作为字典键

6 投票
1 回答
4093 浏览
提问于 2025-04-17 09:58

我刚开始使用 numpy 和它的 matrix 模块(非常非常有用!),我想把一个矩阵对象当作字典的键,所以我检查了一下 matrix 是否实现了 __hash__ 方法:

>>> from numpy import matrix
>>> hasattr(matrix, '__hash__')
True

结果是有的!太好了,这意味着它可以作为字典的键:

>>> m1 = matrix('1 2 3; 4 5 6; 7 8 9')
>>> m1
matrix([[1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]])
>>> m2 = matrix('1 0 0; 0 1 0; 0 0 1')
>>> m2
matrix([[1, 0, 0],
        [0, 1, 0],
        [0, 0, 1]])
>>> matrix_dict = {m1: 'first', m2: 'second'}

成功了!接下来,我们继续测试:

>>> matrix_dict[m1]
'first'
>>> matrix_dict[matrix('1 2 3; 4 5 6; 7 8 9')]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: matrix([[1, 2, 3],
                  [4, 5, 6],
                  [7, 8, 9]])

什么?所以,对于同一个矩阵是可以的,但对于另一个内容完全相同的矩阵却不行?我们来看看 __hash__ 返回了什么:

>>> hash(m1)
2777620
>>> same_as_m = matrix('1 2 3; 4 5 6; 7 8 9')
>>> hash(same_as_m)
-9223372036851998151
>>> hash(matrix('1 2 3; 4 5 6; 7 8 9')) # same as m too
2777665

所以,numpy 中的 matrix__hash__ 方法对于同一个 matrix 返回了不同的值。

这对吗?这是不是意味着它不能用作字典的键?如果不能用,那它为什么还要实现 __hash__ 呢?

1 个回答

9

把一个可变对象当作字典的键是错误的,因为一旦你改变了这个对象的数据,它的哈希值就应该改变,但插入时使用的值却会保持不变。

在我测试的过程中,Python 3.2.2的numpy会抛出一个类型错误:

TypeError: unhashable type: 'matrix'

但是在Python 2.7中,它仍然允许进行哈希操作,不过当你改变数据时,哈希值却不会改变。因此,把它作为字典的键就没什么用,因为很多matrix对象被添加到字典中时哈希值相同,这样会导致哈希表的性能下降,插入操作的复杂度会变成O(n^2),而不是O(1)

也许他们没有去掉哈希值是为了避免破坏Python 2.x中的某些API,但你可千万别依赖这个!

撰写回答