Python - 类 __hash__ 方法和设置

Posted

技术标签:

【中文标题】Python - 类 __hash__ 方法和设置【英文标题】:Python - class __hash__ method and set [duplicate] 【发布时间】:2016-11-20 16:20:21 【问题描述】:

我正在使用python 类的set()__hash__ 方法来防止在集合中添加相同的哈希对象。根据python data-model document,set() 将相同的哈希对象视为相同的对象,只需添加一次。

但它的行为不同如下:

class MyClass(object):

    def __hash__(self):
        return 0

result = set()
result.add(MyClass())
result.add(MyClass())

print(len(result)) # len = 2

在字符串值的情况下,它可以正常工作。

result.add('aida')
result.add('aida')

print(len(result)) # len = 1

我的问题是:为什么相同的哈希对象在集合中不一样?

【问题讨论】:

【参考方案1】:

您的阅读不正确。 __eq__ 方法用于相等性检查。文件只是说明__hash__ 值也必须与a == b(即a.__eq__(b))为真的2 个对象ab 相同。

这是一个常见的逻辑错误:a == b 为真implieshash(a) == hash(b) 也为真。然而,一个暗示并不一定意味着equivalence,除了先前,hash(a) == hash(b) 将意味着a == b

要使MyClass 的所有实例彼此相等,您需要为它们提供__eq__ 方法;否则 Python 将改为比较它们的身份。这可能会:

class MyClass(object):
    def __hash__(self):
        return 0
    def __eq__(self, other):
        # another object is equal to self, iff 
        # it is an instance of MyClass
        return isinstance(other, MyClass)

现在:

>>> result = set()
>>> result.add(MyClass())
>>> result.add(MyClass())
1

实际上,您会将__hash__ 基于用于__eq__ 比较的对象的那些属性,例如:

class Person
    def __init__(self, name, ssn):
        self.name = name
        self.ssn = ssn

    def __eq__(self, other):
        return isinstance(other, Person) and self.ssn == other.ssn

    def __hash__(self):
        # use the hashcode of self.ssn since that is used
        # for equality checks as well
        return hash(self.ssn)

p = Person('Foo Bar', 123456789)
q = Person('Fake Name', 123456789)
print(len(p, q)  # 1

【讨论】:

有什么方法可以选择集合中的对象吗?如更喜欢'Foo Bar' 而不是'Fake Name' @alliefitter 没有。不使用哈希和 eq【参考方案2】:

Set 需要 两个 方法来使对象可散列化:__hash____eq__。当两个实例被认为相等时,必须返回相同的哈希值。如果集合中存在散列并且该实例被认为等于集合中具有相同散列的实例之一,则认为该实例已经存在于集合中。

你的类没有实现__eq__,所以使用默认的object.__eq__,它只在obj1 is obj2也为真时返回真。换句话说,只有当两个实例完全相同时,它们才被认为是相等的。

仅仅因为它们的哈希值匹配,并不能使它们就集合而言是唯一的;即使具有不同散列的对象也可能最终在同一个散列表槽中,因为使用了散列相对于表大小的 模数

添加您的自定义 __eq__ 方法,当两个实例应该相等时返回 True

def __eq__(self, other):
    if not isinstance(other, type(self)):
        return False
    # all instances of this class are considered equal to one another
    return True

【讨论】:

很好奇为什么它需要这两种方法,在 eq 之前先检查哈希是否有一些性能优势? @Davos 集实现为hash tables。是的,使用哈希表意味着集合成员资格测试需要 O(1) 恒定时间,而列表需要 O(N),因此这是一种性能选择。

以上是关于Python - 类 __hash__ 方法和设置的主要内容,如果未能解决你的问题,请参考以下文章

Python面向对象编程第13篇 特殊方法之__hash__

Python魔法方法(10):__hash__(self) 方法

python中默认的__hash__是啥?

如何在python中实现一个好的__hash__函数[重复]

python_封装redis_hash方法

python tips:作为dict的key的类