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'
Python的__hash__函数和__eq__函数的更多相关文章
- 实习小记-python 内置函数__eq__函数引发的探索
		乱写__eq__会发生啥?请看代码.. >>> class A: ... def __eq__(self, other): # 不论发生什么,只要有==做比较,就返回True ... ... 
- 【转】实习小记-python 内置函数__eq__函数引发的探索
		[转]实习小记-python 内置函数__eq__函数引发的探索 乱写__eq__会发生啥?请看代码.. >>> class A: ... def __eq__(self, othe ... 
- python内置函数与匿名函数
		内置函数 Built-in Functions abs() dict() help() min() setattr() all() dir() hex() next() slice() any() d ... 
- 第四章:Python基础の快速认识內置函数和操作实战
		本課主題 內置函数介紹和操作实战 装饰器介紹和操作实战 本周作业 內置函数介紹和操作实战 返回Boolean值的內置函数 all( ): 接受一個可以被迭代的對象,如果函数裡所有為真,才會真:有一個是 ... 
- python内置函数,匿名函数
		一.匿名函数 匿名函数:为了解决那些功能很简单的需求而设计的一句话函数 def calc(n): return n**n print(calc(10)) #换成匿名函数 calc = lambda n ... 
- Python之旅Day3   文件操作  函数(递归|匿名|嵌套|高阶)函数式编程   内置方法
		知识回顾 常见五大数据类型分类小结:数字.字符串.列表.元组.字典 按存值个数区分:容器类型(列表.字典.元组) 标量原子(数字.字符串) 按是否可变区分:可变(列表.字典) 不可变(数字.字符串.元 ... 
- 万恶之源 - Python装饰器及内置函数
		装饰器 听名字应该知道这是一个装饰的东西,我们今天就来讲解一下装饰器,有的铁子们应该听说,有的没有听说过.没有关系我告诉你们这是一个很神奇的东西 这个有多神奇呢? 我们先来复习一下闭包 def fun ... 
- Python开发基础-Day11内置函数补充、匿名函数、递归函数
		内置函数补充 python divmod()函数:把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b) 语法: divmod(a, b) #a.b为数字,a为除数,b ... 
- Py修行路  python基础 (十三)匿名函数 与 内置函数
		一.匿名函数 1.定义: 匿名函数顾名思义就是指:是指一类无需定义标识符(函数名)的函数或子程序. 2.语法格式:lambda 参数:表达式 lambda语句中,开头先写关键字lambda,冒号前是 ... 
随机推荐
- LC 712. Minimum ASCII Delete Sum for Two Strings
			Given two strings s1, s2, find the lowest ASCII sum of deleted characters to make two strings equal. ... 
- Proxmox
			vmware: vmware 12 pro proxmox 下载地址 往下会比较麻烦一点,这里就不做展示了(仅供参考) 
- Hadoop完全分布式安装配置完整过程
			一. 硬件.软件准备 1. 硬件设备 为了方便学习Hadoop,我采用了云服务器来配置Hadoop集群.集群使用三个节点,一个阿里云节点.一个腾讯云节点.一个华为云节点,其中阿里云和腾讯云都是通过使用 ... 
- 关于 About
			关于我 我是 Ivy,目前武汉大学 GIS 专业在读硕士研究生,业余渣程序媛. 写了一些不起眼的代码(参看我的 GitHub),做了一些不起眼的小研究(参看我的 ResearchGate). 关于本站 ... 
- Python基于回溯法解决01背包问题实例
			Python基于回溯法解决01背包问题实例 这篇文章主要介绍了Python基于回溯法解决01背包问题,结合实例形式分析了Python回溯法采用深度优先策略搜索解决01背包问题的相关操作技巧,需要的朋友 ... 
- jQuery页面加载完毕事件及jQuery与JavaScript的比较
			1.jQuery概述 jQuery是一个JavaScript库,它集成了JavaScript.DOM.CSS和Ajax,简化了JavaScript编程,提倡write less, do more. 2 ... 
- 【DSP开发】【计算机视觉】EMCV:可在DSP上运行的OpenCV
			EMCV:可在DSP上运行的OpenCV EMCV项目主页: http://sf.net/projects/emcv EMCV全称为Embedded Computer Vision Library,是 ... 
- docker-compose 部署elk+解决时间不对导致kibana找不到logstash定义的index + docker-compose安装
			1.拉代码 git clone https://github.com/deviantony/docker-elk.git 2.docker-compose配置文件 [root@host7 docker ... 
- 语言模型评价指标Perplexity
			在信息论中,perplexity(困惑度)用来度量一个概率分布或概率模型预测样本的好坏程度.它也可以用来比较两个概率分布或概率模型.(应该是比较两者在预测样本上的优劣)低困惑度的概率分布模型或概率模型 ... 
- nslookup 命令
			NAME nslookup - query Internet name servers interactively SYNOPSIS nslookup [-option] [name | -] [se ... 
