Python学习笔记 | 关于python数据对象 hashable & unhashable 的理解
写在前面
Hash
(哈希、散列)是一个将大体量数据转化为很小数据的过程,甚至可以仅仅是一个数字,以便我们可以在O(1)
的时间复杂度下查询它,所以,哈希对高效的算法和数据结构很重要。
immutable
(不可改变性)是指一些对象在被创建之后不会因为某些方式改变,特别是针对任何可以改变哈希对象的哈希值的方式。
由于hash key
必须是不可变(immutable
)的,对应的hash value
才能是不变,所以不可变
(immutable
)和可哈希
(hashable
)是有关系的。如果hash key
允许改变,那么像hashtable
这样数据结构的对象将会改变,整个hash映射就都会失效。
具体以例来看,元组(tuple
)对象是不可变的(immutable
),字典(dict
)的键(key
)必须是可以哈希的(hashable
)。
hashable & unhashable
官方文档:
如果一个对象在其生命周期内有一个固定不变的哈希值 (这需要__hash__()
方法) 且可以与其他对象进行比较操作 (这需要__eq__()
或 __cmp__()
方法) ,那么这个对象就是可哈希对象 (hashable
) 。可哈希对象必须有相同的哈希值才算作相等。
由于字典 (dict
) 的键 (key
) 和集合 (set
) 元素使用到了哈希值,所以只有可哈希 (hashable
) 对象才能被用作字典的键和集合的元素。
所有python内置的不可变(immutable
)对象(tuple
等)都是可哈希的,同时,可变容器 (比如:列表 (list
) 或者字典 (dict
) ) 都是不可哈希的。用户自定义的类的实例默认情况下都是可哈希的;它们跟其它对象都不相等 (除了它们自己) ,它们的哈希值来自id()
方法。
mutable & immutable
(转自 https://www.jianshu.com/p/49f940b2c03e)
首先要明白的是当我们在聊可变与不可变对象时,我们聊的是Python的内置对象。自己定义的对象通常我们不去讨论它是不是可变的,毕竟Python本身是一门动态语言,需要的话我们随时可以给自己定义的这个对象添加其它的属性和方法。
提到Python内置的不可变对象我们能想到的往往有数字、字符串、元组等,提到Python内置的可变对象我们能想到的又有列表、字典等。我们是依据什么把其中的一些对象归于可变,又把另一些归于不可变的呢?
其实这种归类的办法很简单:当我们改变一个对象的值的时候,如果能维持其id值不变,我们就说这个对象是可变,否则我们就说这个对象不可变。
实例检测
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# unhashable 可变对象
# 如list、dict、set:同值不同址,不同值同址
# hashable 不可变对象
# 如int、str、char、tuple:同值同址,不同值不同址
# 怎么判断可变不可变 ?
# 改个值,看id是不是一样,id一样的为可变,则不可哈希。id出现变化,则为不可变,可哈希
# list
L = [1, 2, 3]
L2 = [1, 2, 3]
print('id(L)', id(L))
print('id(L2)', id(L2))
L[0] = 4
print('id(L)', id(L)) # unhashable
'''---------------------
id(L) 2763485176456
id(L2) 2763485176520
id(L) 2763485176456
---------------------'''
# dict
D = {'A':100, 'A-':90, 'B':80, 'C':70}
D2 = {'A':100, 'A-':90, 'B':80, 'C':70}
print('id(D)', id(D))
print('id(D2)', id(D2))
D['A'] = 99
print('id(D)', id(D)) # unhashable
'''---------------------
id(D) 2763485641608
id(D2) 2763485641680
id(D) 2763485641608
---------------------'''
# set
S = set([1, 2, 3])
S2 = set([1, 2, 3])
print('id(S)', id(S))
print('id(S2)', id(S2))
S.remove(1)
print('id(S)', id(S)) # unhashable
'''---------------------
id(S) 1905131096776
id(S2) 1905131094088
id(S) 1905131096776
---------------------'''
# int
a = 666
b = 666
print('hash(a):', hash(a))
print('hash(b):', hash(b))
print('id(a)', id(a))
print('id(b)', id(b))
a = 555
print('hash(a):', hash(a))
print('id(a)', id(a)) # hashable
'''---------------------
hash(a): 666
hash(b): 666
id(a) 1905130526128
id(b) 1905130526128
hash(a): 555
id(a) 1905131082192
---------------------'''
# float
a = 1.2
b = 1.2
print('hash(a):', hash(a))
print('hash(b):', hash(b))
print('id(a)', id(a))
print('id(b)', id(b))
a = 1.1
print('hash(a):', hash(a))
print('id(a)', id(a)) # hashable
'''---------------------
hash(a): 461168601842738689
hash(b): 461168601842738689
id(a) 2682206597600
id(b) 2682206597600
hash(a): 230584300921369601
id(a) 2682206597624
---------------------'''
# str
c = 'ZJ'
d = 'ZJ'
print('id(c)', id(c))
print('id(d)', id(d))
c = 'YF'
print('id(a)', id(c)) # hashable
'''---------------------
hash(c): 3106900240887856397
hash(d): 3106900240887856397
id(c) 1642181677384
id(d) 1642181677384
hash(c): -2749512413466868010
id(c) 1642181677608
---------------------'''
# tuple
T = (1, 2, 3)
T2 = (1, 2, 3)
print('hash(T):', hash(T))
print('hash(T2):', hash(T2))
print('id(T)', id(T))
print('id(T2)', id(T2))
'''---------------------
hash(T): 2528502973977326415
hash(T2): 2528502973977326415
id(T) 2325794115656
id(T2) 2325794115656
---------------------'''
# tuple2
T = (1, [1, 2], 3)
T2 = (1, [1, 2], 3)
print('id(T)', id(T))
print('id(T2)', id(T2))
T[1][0] = 4
print('id(T)', id(T)) # hashable
'''---------------------
id(T) 2979746926664
id(T2) 2979747407048
id(T) 2979746926664
---------------------'''
# 补充说明
# 虽然字符串有个replace()方法,也确实变出了'Abc',但变量a最后仍是'abc'
a = 'abc'
print('a:', a)
print('a:', a.replace('a', 'A'))
print('a:', a)
'''---------------------
a: abc
a: Abc
a: abc
---------------------'''
# 这是因为 a.replace('a', 'A') 相当于
b = a.replace('a', 'A') # replace方法创建了一个新字符串'Abc'并返回
# Summary:
# 对于不变对象来说, 调用对象自身的任意方法, 也不会改变该对象自身的内容。
# 相反, 这些方法会创建新的对象并返回, 这样, 就保证了不可变对象本身永远是不可变的。
>>> class A:
... pass
...
>>> cls_a = A()
>>> cls_b = A()
>>> cls_a
<A object at 0x000001890DB69898>
>>> cls_b
<A object at 0x000001890DB6EDD8>
>>> cls_a.__hash__()
-9223371931345262199
>>> cls_b.__hash__()
-9223371931345260835
>>> id(cls_a)
1688152217752
>>> id(cls_b)
1688152239576
# 这里两个对象(cls_a和cls_b)哈希值和id都不一样
# 由于用户自定义的类的实例其哈希值与id有关,所以id值和哈希值都不相同,就如官方文档里说的,实例只跟自己相等。
Python内置的可哈希对象可以使用hash()
或者__hash__()
方法来查看它的哈希值,如:a.__hash__()
或者hash(a)
,而id()
函数用于获取对象的内存地址。
后续思考
使用key-value
存储结构的dict
在Python中非常有用,选择不可变对象
作为key
很重要,最常用的key
是字符串
。
tuple
虽然是不可变对象
,但试试把(1, 2, 3)
和(1, [2, 3])
放入dict
或set
中
# tuple是不变对象,试试把(1, 2, 3)和(1, [2, 3])放入dict或set中
T = (1, 2, 3)
T2 = (1, [2, 3])
D = {T: 100}
print('D:', D)
'''-----------------
D: {(1, 2, 3): 100}
-----------------'''
# D2 = {T2: 100} # TypeError: unhashable type: 'list'
# print('D2:', D2)
S = set([T])
print('S:', S)
'''-----------------
S: {(1, 2, 3)}
-----------------'''
# S = set([T2]) # TypeError: unhashable type: 'list'
# print('S:', S)
参考文章
- 聊一聊Python中的hashable和immutable
- python-对象之hashable&unhashable与immutable&mutable
- What do you mean by hashable in Python?
Python学习笔记 | 关于python数据对象 hashable & unhashable 的理解的更多相关文章
- python学习笔记:python对象
一.python对象 python使用对象模型来存储数据,构造任何类型的值都是一个对象.所有的python对象都拥有三个特性:身份.类型和值. 身份:每个对象都有一个唯一的身份标识自己,对象的身份可以 ...
- Python学习笔记之—— File(文件) 对象常用函数
file 对象使用 open 函数来创建,下表列出了 file 对象常用的函数: 1.file.close() close() 方法用于关闭一个已打开的文件.关闭后的文件不能再进行读写操作, 否则会触 ...
- 吴裕雄--天生自然python学习笔记:WEB数据抓取与分析
Web 数据抓取技术具有非常巨大的应用需求及价值, 用 Python 在网页上收集数据,不仅抓取数据的操作简单, 而且其数据分析功能也十分强大. 通过 Python 的时lib 组件中的 urlpar ...
- Python学习笔记:外部数据的输入、存储等操作
查看current工作路径: >>> import os >>> os.getcwd() 'D:\\python' 更改工作路径: >>> os. ...
- python 学习笔记 9 -- Python强大的自省简析
1. 什么是自省? 自省就是自我评价.自我反省.自我批评.自我调控和自我教育,是孔子提出的一种自我道德修养的方法.他说:“见贤思齐焉,见不贤而内自省也.”(<论语·里仁>)当然,我们今天不 ...
- python学习笔记(一):python简介和入门
最近重新开始学习python,之前也自学过一段时间python,对python还算有点了解,本次重新认识python,也算当写一个小小的教程.一.什么是python?python是一种面向对象.解释型 ...
- Python学习笔记 - day12 - Python操作NoSQL
NoSQL(非关系型数据库) NoSQL,指的是非关系型的数据库.NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称.用于超大规模数据的存储.(例如 ...
- python 学习笔记一——Python安装和IDLE使用
好吧,一直准备学点啥,前些日子也下好了一些python电子书,但之后又没影了.年龄大了,就是不爱学习了.那就现在开始吧. 安装python 3 Mac OS X会预装python 2,Linux的大多 ...
- python学习笔记(python简史)
一.python介绍 python的创始人为吉多·范罗苏姆(Guido van Rossum) 目前python主要应用领域: ·云计算 ·WEB开发 ·科学运算.人工智能 ·系统运维 ·金融:量化交 ...
随机推荐
- Redis错误:jedis.exceptions.JedisDataException: ERR Client sent AUTH, but no password is set
原文链接:http://blog.csdn.net/rchm8519/article/details/48347797 redis.clients.util.Pool.getResource(Pool ...
- centos安装ss教程
在CentOS 6.6上安装ShadowSocks服务端 1.查看系统[root@localhost ~]# cat /etc/issue CentOS release 6.6 (Final) [ro ...
- P4819 [中山市选]杀人游戏
题目描述 一位冷血的杀手潜入Na-wiat,并假装成平民.警察希望能在NN个人里面,查出谁是杀手.警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人,谁是杀手,谁是平民.假如查 ...
- P1968
题目背景 此处省略maxint+1个数 题目描述 在以后的若干天里戴维将学习美元与德国马克的汇率.编写程序帮助戴维何时应买或卖马克或美元,使他从100美元开始,最后能获得最高可能的价值. 输入输出格式 ...
- linux命令(47):rmdir命令
1.命令格式: rmdir [选项]... 目录... 2.命令功能: 该命令从一个目录中删除一个或多个子目录项,删除某目录时也必须具有对父目录的写权限. 3.命令参数: - p 递归删除目录dirn ...
- hadoop 分布式环境安装
centos 多台机器免密登录 hadoop学习笔记(五)--全分布模式下SSH免密码登陆的实现 参考安装教程 Hadoop-2.7.4 集群快速搭建 启动hadoop cd /opt/soft/ha ...
- Highcharts创建一个简单的柱状图
新建一个html文件,将highcharts引入到你的页面后,通过两个步骤我们就可以创建一个简单的图表了. 1.创建div容器 在页面的 body部分创建一个div,并指定div 的 id,高度和宽度 ...
- python字符编码与解码 unicode,str
解释以下几个问题: (1)python2中str和unicode是两种字符串类型,与字符编码方式是什么关系? (2)str和unicode是怎么相互转换的? (3)'\x...':'\u...', ' ...
- 微信小程序-二维码汇总
小程序二维码在生活中的应用场景很多,比如营销类一物一码,扫码开门,扫码付款等...小程序二维码分两种? 1.普通链接二维码 即跟普通的网站链接生成的二维码是一个意思,这种二维码的局限性如下: 对于普通 ...
- laravel中新增路由文件
随着业务的发展,前后台和不同平台的代码都写在一个路由文件里面会非常庞杂,这时候就诞生了拆分路由文件的需求,好在Lavravel给我们提供了支持: 1.在routes文件夹中添加新的路由文件如:admi ...