21

我正在使用类set()__hash__方法python来防止在集合中添加相同的哈希对象。根据python 数据模型文档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

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

4

2 回答 2

32

你的阅读不正确。该__eq__方法用于相等性检查。文档只是说明__hash__2 个对象的值也必须相同,并且a( ie ) 为真。ba == ba.__eq__(b)

这是一个常见的逻辑错误:a == b为真意味着hash(a) == hash(b)也为真。但是,蕴涵并不一定意味着等价,即除了先验之外,hash(a) == hash(b)还意味着a == b

要使MyClasscompare 的所有实例彼此相等,您需要__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
于 2016-07-18T06:59:14.797 回答
12

集合需要两种方法来使对象可散列:__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
于 2016-07-18T06:59:05.060 回答