网站首页 > 基础教程 正文
引言
没有Python程序不使用字典,即使不直接出现在我们自己编写的代码中,我们也会在无形中、间接用到字典(日用而不自知)。所以,字典(dict)是Python实现的基石。
一些Python核心结构在内存中以字典的形式存在,比如:类和实例属性、模块命名空间、函数的关键字参数等。
此外,__builtins__.__dict__中存储着所有内置类型、对象和函数。
所以,本文就字典相关的内容,做一个补充说明。
什么是“可哈希”
前面的文章中关于字典的介绍时,我们已经有所提及,字典的键必须是可哈希的。
“可哈希”在Python中是这样说明的:
如果一个对象的哈希值在整个生命周期内永不改变(依托__hash__()方法),而且可与其他对象进行比较(依托__eq__()方法),那么这个对象就是可哈希的。
两个可哈希对象仅当哈希值相同时才相等。
1、数值类型、不可变的扁平类型(比如str和bytes)均是可哈希的。
2、如果容器类型是不可变的,且所含的对象均是可哈希的,那么容器类型自身也是可哈希的。所以,tuple对象是不可变的,仅当tuple中所有元素项均可哈希,这个特定的tuple对象才是可哈希的。
3、frozenset对象全部是可哈希的。
4、用户自定义的类型,默认是可哈希的,因为自定义类型的哈希值基于id(),而且继承自obejct类的__eq__()方法只不过是比较对象ID。如果自己实现了__eq__()方法,根据对象内部状态进行比较,那么仅当__hash__()方法始终返回同一个哈希值时,对象才是可哈希的。
下面是关于上面4条是否可哈希规则的验证代码:
# 容器对象本身不可变,元素均为可哈希,则容器可哈希
t1 = (1, 2, (3, 4, 5))
print(hash(t1))
# 存在不可哈希元素,hash()时会报错
t2 = (1, 2, [3, 4, 5])
# print(hash(t2))
# frozenset可哈希
t3 = (1, 2, frozenset([3, 4, 5]))
print(hash(t3))
# 自定义对象,没有重写__eq__()和__hash__(),所以默认可哈希
class MyValue1:
def __init__(self, value):
self.value = value
mv1 = MyValue1(10)
print(hash(mv1))
print(id(mv1))
# 自定义了__eq__,没有重写__hash__,所以不可哈希
class MyValue2:
def __init__(self, value):
self.value = value
def __eq__(self, other):
return self.value == other.value
mv2 = MyValue2(10)
# 不可哈希,报错
# print(hash(mv2))
# 同时重写__eq__()和__hash__(),可哈希
class MyValue3:
def __init__(self, value):
self.value = value
def __eq__(self, other):
return self.value == other.value
def __hash__(self):
return hash(self.value)
mv3 = MyValue3(10)
print(hash(mv3))
字典视图
在前面的文章中,我们通过字典dict对象的实例方法:keys()、values()、items()进行字典数据的遍历。
从dict的定义描述中可以看到,这些方法返回的均是基于dict内部实现使用的数据结构的视图:
这些视图是动态视图,根据dict对象的变化同步更新:
d1 = {x: x ** 2 for x in range(10)}
print(d1)
keys = d1.keys()
print(type(keys))
values = d1.values()
print(type(values))
items = d1.items()
print(type(items))
print(keys)
d1[10] = 100
print(keys)
此外,除了dict_values类是相对简单的字典视图,dict_keys和dict_items还实现了多个集合方法,基本与frozenset类相当。
字典的实现
由于字典的关键作用,Python对字典做了高度优化,而且一直不断在改进。Python中字典能够如此高效,主要归功于其底层的哈希表。
此外,内置类型中的set和frozenset也基于哈希表来实现。
虽然基于哈希表的实现,带来了更高的效率。但是,也对使用上带来一些影响:
1、键必须是可哈希的。
2、通过键访问项的速度非常快,通过计算键的哈希值可以直接定位键,然后找出索引在哈希表中的偏移量,稍微尝试几次就能找到匹配到的条目。
3、在CPython3.6中,dict的内存布局更为紧凑,顺带了一个副作用是键的顺序得以保留。当然,Python3.7开始,将这个副作用写入了标准,正式支持保留键的插入顺序。
4、尽管使用了紧凑布局,但是,字典仍然需要占用大量内存,这是不可避免的。
5、为了节省内存,不要在__init__()方法之外创建实例属性。因为,Python默认在特殊的__dict__属性中存储实例属性,而这个属性是一个字典,依附在各个实例上。自Python3.3起,类的实例可以共用一个哈希表,随类一起存储。如果新的实例与__init__()返回的第一个实例拥有相同的属性名称,那么新实例的__dict__属性就共享这个哈希表,仅以指针数组的形式存储新实例的属性值。如果__init__()方法执行完毕后再添加实例属性,Python就不得不为这个实例的__dict__属性创建一个新哈希表。
猜你喜欢
- 2025-01-07 Python从入门到放弃-详解列表、元组和字典
- 2025-01-07 python 中字典如何进行复制
- 2025-01-07 python入门023:字典嵌套
- 2025-01-07 掌握Python字典的12个例子
- 2025-01-07 使用Python 获取多级字典(Json)格式所有Key、Value
- 2025-01-07 简单学Python——字典的操作1(增加、更改和删除字典元素)
- 2025-01-07 深入了解python字典的有序特性
- 2025-01-07 如何在 Python 中以列表形式返回字典的键
- 2025-01-07 解锁Python字典合并:多种方法解析
- 2025-01-07 Python 动手练:字典
- 05-22Linux cron任务计划
- 05-22测试人员如何在linux服务器中查询mysql日志?
- 05-22Nginx命令最全详解(29个最常用命令)
- 05-22初识自动化网络编排器NSO,轻松配置复杂的多厂商网络
- 05-22Nacos在企业生产中如何使用集群环境?
- 05-22如何从 MySQL 错误日志中排查数据库故障
- 05-22Linux面试最高频的5个基本问题
- 05-22linux网卡混杂模式
- 最近发表
- 标签列表
-
- jsp (69)
- gitpush (78)
- gitreset (66)
- python字典 (67)
- dockercp (63)
- gitclone命令 (63)
- dockersave (62)
- linux命令大全 (65)
- pythonif (86)
- location.href (69)
- dockerexec (65)
- tail-f (79)
- deletesql (62)
- c++模板 (62)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- console.table (62)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)