如何在python中实现一个好的__hash__函数

aba*_*gat 93 python hash

实现具有多个属性的类时(如下面的玩具示例),处理散列的最佳方法是什么?

我猜测,__eq____hash__应该是一致的,但如何实现适当的哈希函数,能够处理所有属性?

class AClass:
  def __init__(self):
      self.a = None
      self.b = None

  def __eq__(self, other):
      return other and self.a == other.a and self.b == other.b

  def __ne__(self, other):
    return not self.__eq__(other)

  def __hash__(self):
      return hash((self.a, self.b))
Run Code Online (Sandbox Code Playgroud)

我读到这个问题,元组是可以清洗的,所以我想知道上面的例子是否合情合理.是吗?

adw*_*adw 71

__hash__应该为相等的对象返回相同的值.它也不应该在对象的生命周期内改变; 通常,您只为不可变对象实现它.

一个简单的实现将是公正的return 0.这总是正确的,但表现不好.

你的解决方案,返回一个属性元组的哈希是好的.但请注意,您不需要列出__eq__在元组中比较的所有属性.如果某些属性对于不等对象通常具有相同的值,则只需将其保留.不要使哈希计算比它需要的更昂贵.

编辑:我建议不要使用xor来混合哈希.当两个不同的属性具有相同的值时,它们将具有相同的散列,并且对于xor,这些将取消彼此.元组使用更复杂的计算混合散列,见tuplehashtupleobject.c.

  • 'return 0`哈希函数的+1 - 我一直认为其他任何事情都是过早的优化:-).(我只是半开玩笑). (5认同)
  • @BjörnPollex您可以在`__hash__`中缓存值,而不是在`__init__`中执行此操作.这样,如果从不调用`__hash__`,你就不会浪费时间或记忆.我假设检查值是否已经缓存是不是很昂贵呢?(不确定通过异常或显式`if`是否最好). (4认同)
  • 正如您所说的哈希函数通常只对不可变对象有意义.因此,可以在`__init__`中计算一次哈希值. (3认同)
  • 它没有在dict或list之类的东西中实现,理由是改变已经属于的对象的哈希值,例如,集合会对集合的内部数据结构造成严重破坏. (3认同)

S.L*_*ott 14

文档 object.__hash__(self)

唯一需要的属性是比较相等的对象具有相同的哈希值; 建议以某种方式将对象的组件的哈希值混合在一起(例如,使用异或),这些哈希值也在对象的比较中起作用.

def __hash__(self):
    return hash(self.a) ^ hash(self.b)
Run Code Online (Sandbox Code Playgroud)

  • 为什么不只是哈希一个元组值?hash((self.a,self.b)) (13认同)
  • 它会起作用,但是如果你交换`self.a`和`self.b`然后你会得到相同的哈希,而它将是另一个"对象",这是很糟糕的. (4认同)
  • 请注意,(幸运的是)[Python 3](https://docs.python.org/3/reference/datamodel.html#object.__hash__)或[Python 2](https ://docs.python.org/2/reference/datamodel.html#object.__hash__)文档。 (4认同)
  • 对于那些感兴趣的人,这是导致删除XOR建议的错误:https://bugs.python.org/issue28383 (4认同)

max*_*max 12

写作很危险

def __eq__(self, other):
  return other and self.a == other.a and self.b == other.b
Run Code Online (Sandbox Code Playgroud)

因为如果你的rhs(ie,other)对象的计算结果为布尔值False,那么它永远不会与任何东西相等!

此外,您可能需要仔细检查是否other属于类或子类AClass.如果没有,您将获得异常AttributeError或误报(如果其他类恰好具有匹配值的相同命名属性).所以我建议重写__eq__为:

def __eq__(self, other):
  return isinstance(other, self.__class__) and self.a == other.a and self.b == other.b
Run Code Online (Sandbox Code Playgroud)

如果你想要一个非常灵活的比较,只要属性按名称匹配,就可以比较不相关的类,你仍然希望至少避免AttributeError并检查other它没有任何其他属性.你如何做到这取决于情况(因为没有标准的方法来查找对象的所有属性).

  • 有用的信息,但与关于散列的主要问题无关. (4认同)