Hash in Python

[Problem background] I customized the Object type. When I used set() to judge the weight, I found that the overload did __eq__not work. I always thought it was different.

[Cause of the problem] When a custom Object is used as a set () collection element, because the set belongs to the hash algorithm data structure, the hash is first judged when judging the weight, and only when the hash is the same will continue to be called __eq__to judge the weight. The same is true for other hash data structures.

1. When the magic method is __hash__called

Please note that this __hash__magic method:

(1) Called by the built-in function hash()

(2) a set of hash hash type operation member itself: set(), frozenset([iterable]),dict(**kwarg)

2 Examples of application scenarios

2.1 Only use __eq__judgment, no need to overload__hash__

It will only be called when the objects are compared using the parity symbol __eq__. At this time, the __hash__correct result can be obtained without overloading :

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
class Student(object):
    def __init__(self, name, age):
        self._name = name
        self._age = age

    def __hash__(self):
        print 'Call __hash__'
        
    def __eq__(self, other):
        print 'Call __eq__'
        if not isinstance(other, Student):
            return False
        return self._name == other._name and self._age == other._age

if __name__ == '__main__':
    s1 = Student('aa', 20)
    s2 = Student('aa', 20)
    s3 = Student('bb', 21)
    print s1 == s2
    print s1 == s3

2.2 Comparison strategy of hash structure

In the Python3.X series, if it is only customized __eq__but not defined __hash__, then the Object cannot be used as a hash data structure (set, frozenset, dict) element. Because at this time automatically let __hash__return None;

In the Python2.X series, if it is only customized __eq__but not defined __hash__, it can still be used as a hash data structure (set, frozenset, dict) element. At this time, it will cause a BUG in the comparison. The reasons are as follows:

(1) The hash structure first calls the __hash__comparison Object. The default hash value generation is different (related to the address), so it will always judge that the two Objects are not equal, even if the internal assignments are the same;

(2) When the hash structure is __hash__equal, the __eq__comparison will continue to be called .

class Student(object):
    def __init__(self, name, age):
        self._name = name
        self._age = age

    def __hash__(self):
        print 'Call __hash__'
        return hash(self._name + str(self._age))  # 请注意这里一定要返回hash值,否则None报错

    def __eq__(self, other):
        print 'Call __eq__'
        if not isinstance(other, Student):
            return False
        return self._name == other._name and self._age == other._age

if __name__ == '__main__':
    s1 = Student('aa', 20)
    s2 = Student('aa', 20)
    s3 = Student('bb', 21)
    test_set = set()
    test_set.add(s1)
    print s2 in test_set    # True
    print s3 in test_set    # False

Guess you like

Origin blog.csdn.net/qdPython/article/details/112709966