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,冒号前是 ...
随机推荐
- Simple Cel Shading 钟馗
Made with Unity Unannouced project Character Art by Chris P
- Cortex-M3 异常中断响应与返回
[异常/中断响应]Cortex-M3的异常/中断响应序列包括: 入栈:把8个寄存器的值压入栈. 取向量:从向量表中找出对应的服务程序入口地址. 更新寄存器:更新堆栈指针SP,更新连接寄存器LR,更新程 ...
- LC 302. Smallest Rectangle Enclosing Black Pixels【lock, hard】
An image is represented by a binary matrix with 0 as a white pixel and 1 as a black pixel. The black ...
- Django之缓存配置
01-什么是缓存 缓存(cache),其作用是缓和较慢存储的高频次请求,简单来说,就是加速满存储的访问效率. 02-几种缓存配置 # 内存缓存:local-memory caching CACHES ...
- Postman系列之测试用例管理(二)
实验简介 本文主要讲解Postman对多个测试用例的管理,让测试进行更有序,易于管理. 实验目的 Postman 测试集(Collections)管理的相关内容,它用来保存我们的 Request ,可 ...
- 学习前端第二天心得体会(初步了解HTML5的部分API以及画布Canvas)
一.HTML5部分API 1.选择器querySelector和querySelectorAll 1.1.querySelector:返回文档中匹配指定的CSS选择器的第一元素. document. ...
- docker批量删除容器、镜像(转载)
1.删除所有容器 docker rm `docker ps -a -q` 2.删除所有镜像 docker rmi `docker images -q` 3.按条件删除镜像 没有打标签 docker r ...
- kvm网络虚拟化(vlan,bond,vlan+bond)(3)
一.Linux Bridge网桥管理 网络虚拟化是虚拟化技术中最复杂的部分,也是非常重要的资源. VM2 的虚拟网卡 vnet1 也连接到了 br0 上. 现在 VM1 和 VM2 之间可以通信,同时 ...
- linux常用命令(17)find命令概览
Linux下find命令在目录结构中搜索文件,并执行指定的操作.Linux下find命令提供了相当多的查找条件,功能很强大.由于find具有强大的功能,所以它的选项也很多,其中大部分选项都值得我们花时 ...
- ceph添加/删除OSD
一.添加osd: 当前ceph集群中有如下osd,现在准备新添加osd: (1)选择一个osd节点,添加好新的硬盘: (2)显示osd节点中的硬盘,并重置新的osd硬盘: 列出节点磁盘:ceph-de ...