我注意到,当我使用用户定义的对象(覆盖__hash__
方法)作为Python中字典的键时,查找时间至少增加了5倍。
即使当我使用非常基本的哈希方法(例如以下示例)时,也会观察到此行为:
class A:
def __init__(self, a):
self.a = a
def __hash__(self):
return hash(self.a)
def __eq__(self, other):
if not isinstance(other, A):
return NotImplemented
return (self.a == other.a and self.__class__ ==
other.__class__)
# get an instance of class A
mya = A(42)
# define dict
d1={mya:[1,2], 'foo':[3,4]}
如果我通过两个不同的键对访问进行计时,则会发现性能存在明显差异
%timeit d1['foo']
导致〜100 ns。而
%timeit d1[mya]
导致〜600 ns。
如果我删除了__hash__
和__eq__
方法的覆盖,则性能与默认对象处于同一水平
有没有办法避免这种性能损失并且仍然实现自定义的哈希计算?
答案 0 :(得分:5)
自定义类的默认CPython __hash__
实现是用C编写的,并使用对象的内存地址。因此,它不必绝对访问对象,并且可以非常快速地完成操作,因为即使在CPU中,它也只是单个整数运算。
该示例中的“非常基础” __hash__
并不像看起来那么简单:
def __hash__(self):
return hash(self.a)
这必须读取a
的属性self
,在这种情况下,我将其称为object.__getattribute__(self, 'a')
,并且它将在{中查找'a'的值{1}}。这已经涉及到计算__dict__
并进行查找。然后,返回的值将传递到hash('a')
。
要回答其他问题:
有没有一种方法可以实现更快的
hash
方法,该方法返回 可预测的值,我的意思是不是每次运行都随机计算 就像对象的内存地址一样?
任何访问对象属性的速度都会比不需要访问属性的实现慢,但是您可以通过使用__slots__
或为该类实现高度优化的C扩展来使属性访问更快。
但是,还有另一个问题:这真的有问题吗?我真的不能相信由于__hash__
缓慢而导致应用程序变慢。除非字典中有数以万计的条目,否则__hash__
仍然应该非常快,但随后,其他所有内容将变慢并要求进行更大的更改...
我做了一些测试,必须进行更正。在这种情况下,使用__hash__
根本无济于事。我的测试实际上表明,在CPython 3.7中,使用__slots__
时,上述类变得稍微慢。