Python - 类__hash__方法和集合

Aid*_*adi 11 python hash set python-datamodel python-3.x

我正在使用类set()__hash__方法python来防止在set中添加相同的哈希对象.根据python数据模型文档,set()将相同的哈希对象视为同一个对象,只需添加一次即可.

但它的行为如下:

class MyClass(object):

    def __hash__(self):
        return 0

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

print(len(result)) # len = 2
Run Code Online (Sandbox Code Playgroud)

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

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

print(len(result)) # len = 1
Run Code Online (Sandbox Code Playgroud)

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

Ant*_*ala 17

你的阅读不正确.该__eq__方法用于相等性检查.该文件只是指出该__hash__值也必须是相同的两个对象a,并b为它a == b(即 a.__eq__(b))是真实的.

这是一个常见的逻辑错误:a == b真实意味着hash(a) == hash(b)也是正确的.然而,暗示并不一定意味着等同,除了先验之外,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)
Run Code Online (Sandbox Code Playgroud)

现在:

>>> result = set()
>>> result.add(MyClass())
>>> result.add(MyClass())
1
Run Code Online (Sandbox Code Playgroud)

实际上,您将基于__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
Run Code Online (Sandbox Code Playgroud)


Mar*_*ers 5

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

您的类没有实现__eq__,因此使用默认值object.__eq__,如果obj1 is obj2也为true ,则仅返回true.换句话说,如果两个实例是完全相同的实例,则它们仅被视为相等.

仅仅因为它们的哈希匹配,就一组而言,它们并不是唯一的; 即使具有不同散列的对象也可以在相同的散列表槽中结束,因为使用散列的模数与表大小相对应.

添加一个自定义__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
Run Code Online (Sandbox Code Playgroud)

  • @Davos 集被实现为[哈希表](https://en.wikipedia.org/wiki/Hash_table)。是的,使用哈希表意味着集合成员资格测试需要 O(1) 常数时间,而列表则需要 O(N) 时间,因此这是一种性能选择。 (2认同)