• Python的__hash__函数和__eq__函数


    Python的__hash__函数和__eq__函数

    可哈希的集合(hashed collections),需要集合的元素实现了__eq____hash__,而这两个方法可以作一个形象的比喻:
    哈希集合就是很多个桶,但每个桶里面只能放一个球。
    __hash__函数的作用就是找到桶的位置,到底是几号桶。
    __eq__函数的作用就是当桶里面已经有一个球了,但又来了一个球,它声称它也应该装进这个桶里面(__hash__函数给它说了桶的位置),双方僵持不下,那就得用__eq__函数来判断这两个球是不是相等的(equal),如果是判断是相等的,那么后来那个球就不应该放进桶里,哈希集合维持现状。

    class Foo:
        def __init__(self, item):
            self.item = item
    
        def __eq__(self, other):
            print('使用了equal函数的对象的id',id(self))
            if isinstance(other, self.__class__):
                return self.__dict__ == other.__dict__
            else:
                return False
        def __hash__(self):
            print('f'+str(self.item)+'使用了hash函数')
            return hash(self.item)       
    f1 = Foo(1)
    f2 = Foo(2)
    f3 = Foo(3)
    fset = set([f1, f2, f3])
    print(fset)
    print()
    f = Foo(3)
    fset.add(f)
    print('f3的id:',id(f3))
    print('f的id:',id(f))
    

    运行结果:

    f1使用了hash函数
    f2使用了hash函数
    f3使用了hash函数
    {<__main__.Foo object at 0x0000023769AB67C0>, <__main__.Foo object at 0x0000023769AC5C10>, <__main__.Foo object at 0x0000023769AC5C40>}
    
    f3使用了hash函数
    使用了equal函数的对象的id 2437019360320
    f3的id: 2437019360320
    f的id: 2437019360368
    

    可见,在将f1,f2,f3加入到set中时,每次都会调用一次__hash__函数。
    由于我定义的___hash__函数是return hash(self.item),所以f和f3找到的桶的位置是同一个位置,因为它俩的item是相同的。当执行fset.add(f)时,f就会调用它自身的__hash__函数,以找到f所属于的桶的位置。但此时桶里已经有别的球了,所以这时候就得用上__eq__来判断两个对象是否相等,从输出可以看出,是已有对象调用__eq__来和后来的对象进行比较(看对象的id)。
    这里如果是删除操作fset.remove(Foo(3)),道理也是一样,先用hash找到桶的位置,如果桶里有球,就判断这两个球是否相等,如果相等就把桶里那个球给扔掉。

    官方解释

    当可哈希集合(set,frozenset,dict)调用hash函数时,应该返回一个int值。唯一的要求就是,如果判断两个对象相等,那么他们的hash值也应该相等。当比较两个对象相等时是使用对象的成员来比较时,建议要把成员弄进元祖里,再得到这个元祖的hash值来比较。

    当class没有定义__eq__()方法时,那么它也不应该定义__hash__()方法。如果它定义了__eq__()方法,却没有定义__hash__()方法,那么这个类的实例就不能在可哈希集合使用。如果一个类定义了一个可变对象(这里应该是指class的成员之一为可变对象),且implement了__eq__()方法,那么这个类就不应该implement hash()方法,因为可哈希对象的实现(implement )要求键值key的hash值是不变的(如果一个对象的hash值改变了,那么它会被放在错误的hash桶里)

    用户定义的类中都有默认的__eq__和__hash__方法;有了它,所有的对象实例都是不等的(除非是自己和自己比较),在做x == y比较时是和这个等价的hash(x) == hash(y)。

    只实现__eq__(错误示范)

    class Foo:
        def __init__(self, item):
            self.item = item
    
        def __eq__(self, other):
            if isinstance(other, self.__class__):
                return self.__dict__ == other.__dict__
            else:
                return False
    
    f1 = Foo(1)
    f2 = Foo(1)
    f3 = Foo(1)
    print(set([f1, f2, f3]))
    

    运行报错:

    Traceback (most recent call last):
      File "c:/Users/Administrator/Desktop/MyFile/MyCoding/Other/hashtest.py", line 14, in <module>
        print(set([f1, f2, f3]))
    TypeError: unhashable type: 'Foo'
    
  • 相关阅读:
    深入理解递归函数的调用过程
    关于字符串和字符数组的再讨论
    返回字符串的长度
    再写静态变量的有效范围
    一道关于返回指针和返回数组名的面试题
    关于TCP/IP的三次握手和四次挥手解释
    C++面向对象的编程(二)
    关于面试宝典中的检测并修改不适合的继承
    argc和argv
    基于C的文件操作(转)
  • 原文地址:https://www.cnblogs.com/kadycui/p/11838590.html
Copyright © 2020-2023  润新知