使用对象的id()作为哈希值

问题描述 投票:3回答:3

像这样实施__hash__是一个坏主意吗?

class XYZ:
    def __init__(self):
        self.val = None

    def __hash__(self):
        return id(self)

我是否设置了可能带来灾难性的东西?

python hash
3个回答
4
投票

__hash__方法必须满足以下要求才能工作:

Forall x,y使x == y,然后hash(x) == hash(y)

在你的情况下,你的类没有实现__eq__,这意味着x == y当且仅当id(x) == id(y),因此你的哈希实现满足上述属性。

但请注意,如果您实现__eq__,那么此实现可能会失败。

另外:拥有一个“有效”的__hash__和一个好的哈希之间存在差异。例如,以下是任何类的有效__hash__定义:

def __hash__(self):
    return 1

一个好的哈希应该尝试统一分布对象,以尽可能避免冲突。通常这需要更复杂的定义。我会避免尝试提出公式,而是依赖python内置的hash函数。

例如,如果你的类有字段abc那么我会使用像__hash__这样的东西:

def __hash__(self):
    return hash((self.a, self.b, self.c))

hash对于元组的定义对于普通情况应该足够好。

最后:你不应该在可变的类中定义__hash__(在用于相等的字段中)。那是因为修改实例会改变它们的哈希值,这会破坏事情。


3
投票

这要么是毫无意义,要么是错误的,这取决于班上的其他人。

如果你的对象使用默认的基于身份的==,那么定义这个__hash__是没有意义的。默认的__hash__也是基于身份的,但速度更快,并且经过调整以避免始终将低位设置为0.使用默认的__hash__会更简单,更高效。

如果你的对象不使用默认的基于身份的==,那么你的__hash__是错误的,因为它会与==不一致。如果你的对象是不可变的,你应该以与__hash__一致的方式实现==;如果你的对象是可变的,你根本不应该实现__hash__(如果需要支持Python 2,则设置__hash__ = None)。


1
投票

这是__hash__的默认实现。请注意,__eq__的填充导致默认的__hash__实现消失。如果你重新实现__hash__,那么任何比较相等的对象必须具有相同的哈希值。

但是,对于不相等的对象,可以使用相同的哈希值。因此,使用返回常量值的哈希实现始终是安全的。但是,效率非常低。

适用于大量用例的一个很好的默认值是返回__eq__方法中使用的属性元组的哈希值。例如。

class XYZ:
    def __init__(self, val0, val1):
        self.val0 = val0
        self.val1 = val1

    def __eq__(self, other):
        return self.val0 == other.val1 and self.val1 == other.val1

    def __hash__(self):
        return hash((self.val0, self.val1))
© www.soinside.com 2019 - 2024. All rights reserved.