Python中字典的key都可以是什么
作者:Inotime
来源:CSDN
原文:https://blog.csdn.net/lnotime/article/details/81192207
答:一个对象能不能作为字典的key,就取决于其有没有__hash__方法。所以所有python自带类型中,除了list、dict、set和内部至少带有上述三种类型之一的tuple之外,其余的对象都能当key。
比如数值/字符串/完全不可变的元祖/函数(内建或自定义)/类(内建或自定义)/方法/包等等你能拿出手的,不过有的实际意义不高。还有数值型要注意,因为两个不同的相等数字可以有相同的哈希值,比如1和1.0。
解释:
代码版本:3.6.3;文档版本:3.6.6
Unlike sequences, which are indexed by a range of numbers, dictionaries are indexed by keys, which can be any immutable type; strings and numbers can always be keys. Tuples can be used as keys if they contain only strings, numbers, or tuples; if a tuple contains any mutable object either directly or indirectly, it cannot be used as a key. You can’t use lists as keys, since lists can be modified in place using index assignments, slice assignments, or methods like append()and extend().
字典的键可以是任意不可变类型,需要注意的是tuple元组作为键时,其中不能以任何方式包含可变对象。
那。。到底什么样的是不可变类型呢?不可能给对象专门标注一个属性是可变类型还是不可变类型啊,这没有任何其他意义,一定是通过其他途径实现的。把list当做键试一下
a = [1, 2, 3]
d = {a: a}
# 第二行报错:
# TypeError: unhashable type: 'list'
报错说list类型是不可哈希的,噢,原来是靠能不能hash来判断的,另外文档下面接着说同一字典中每个键都是唯一的,正好每个对象的哈希值也是唯一的,对应的很好。
It is best to think of a dictionary as an unordered set of key: value pairs, with the requirement that the keys are unique (within one dictionary).
查看源代码可以看到object对象是定义了__hash__方法的,
而list、set和dict都把__hash__赋值为None了
# 部分源码
class object:
""" The most base type """
def __hash__(self, *args, **kwargs): # real signature unknown
""" Return hash(self). """
pass
class list(object):
__hash__ = None
class set(object):
__hash__ = None
class dict(object):
__hash__ = None
那这样的话。。。我给他加一个hash不就能当字典的key了,key不就是可变的了。注意:此处只是我跟着想法随便试,真的应用场景不要用可变类型作为字典的key。
class MyList(list):
"""比普通的list多一个__hash__方法"""
def __hash__(self):
# 不能返回hash(self)
# hash(self)会调用self的本方法,再调用回去,那就没完了(RecursionError)
# 用的时候要注意实例中至少有一个元素,不然0怎么取(IndexError)
return hash(self[0])
l1 = MyList([1, 2]) # print(l1) -> [1, 2]
d = {l1: 'Can?'}
print(d) # --> {[1, 2]: 'Can?'}
l1.append(3)
print(d) # {[1, 2, 3]: 'Can?'}
print(d[l1]) # --> Can?
到这里就可以肯定的说,一个对象能不能作为字典的key,就取决于其有没有__hash__方法。所以所有python自带类型中,目前我已知的除了list、dict、set和内部带有以上三种类型的tuple之外,其余的对象都能当key。而我们自己定义的类,一般情况下都直接间接的和object有关,都带有__hash__方法。
另外我想到,既然字典的键是唯一的,而哈希值也是唯一的,这么巧,键的唯一性不会就是用哈希值来确定的吧?我上一个例子中__hash__方法返回的是0号元素的哈希值,那我直接用相同哈希值的对象是不是就能改变那本来不属于它的字典值呢?
class MyList(list):
def __hash__(self):
return hash(self[0])
l1 = MyList([1, 2]) # print(l1) -> [1, 2]
d = {}
d[l1] = l1
print(d) # {[1, 2]: [1, 2]}
d[1] = 1
print(d) # {[1, 2]: [1, 2], 1: 1}
竟然没有改成功而是新添加了一个键值对,可self[0]就是1啊,哈希值一样啊,怎么会不一样呢?难道要键的值一样才能判断是同一个键吗?重写__eq__方法试一下。
class MyList(list):
def __hash__(self):
return hash(self[0])
def __eq__(self, other):
return self[0] == other
l1 = MyList([1, 2]) # print(l1) -> [1, 2]
d = {}
d[l1] = l1
print(d) # {[1, 2]: [1, 2]}
d[1] = 1
print(d) # {[1, 2]: 1}
这回成功了,那就是__hash__返回值相等,且eq判断也相等,才会被认为是同一个键。那这两个先判断哪个呢?加代码试一下
class MyList(list):
def __hash__(self):
print('hash is run')
return hash(self[0])
def __eq__(self, other):
print('eq is run')
return self[0] == other
l1 = MyList([1, 2]) # print(l1) -> [1, 2]
d = {}
d[1] = 1
d[l1] = 'l1'
print(d)
# 结果:
# hash is run
# eq is run
# {1: 'l1'}
__hash__先执行,另外字典在内存中存储数据的位置和键的hash也是有关的,逻辑上也像印证。先计算hash,找到相对应的那片内存空间,里面没有值的话就直接写入,对于字典来说就是新增键值对;如果里面已经有值了,那就判断新来的键和原来的那里的键是不是相等,相等就认为是一个键,对于字典来说就是更新值,不相等就再开空间,相当于字典新增键值对。
在你验证自己想法的时候可能遇到__hash__和__eq__的一些想不到的麻烦,可以看这里:__hash__和__eq__的继承使用问题
---------------------
Python中字典的key都可以是什么的更多相关文章
- python中字典以key排序,以value排序。以及通过value找key的方式
1.sorted函数首先介绍sorted函数,sorted(iterable,key,reverse),sorted一共有iterable,key,reverse这三个参数. 其中iterable表示 ...
- Python中字典和集合
Python中字典和集合 映射类型: 表示一个任意对象的集合,且可以通过另一个几乎是任意键值的集合进行索引 与序列不同,映射是无序的,通过键进行索引 任何不可变对象都可用作字典的键,如字符串.数字.元 ...
- python接口自动化(九)--python中字典和json的区别(详解)
简介 这篇文章的由来是由于上一篇发送post请求的接口时候,参数传字典(dict)和json的缘故,因为python中,json和dict非常类似,都是key-value的形式,为啥还要这么传参,在群 ...
- python中字典和json的区别
python中,json和dict非常类似,都是key-value的形式,而且json.dict也可以非常方便的通过dumps.loads互转 定义 python中,json和dict非常类似,都是k ...
- python中字典的循环遍历的两种方式
开发中经常会用到对于字典.列表等数据的循环遍历,但是python中对于字典的遍历对于很多初学者来讲非常陌生,今天就来讲一下python中字典的循环遍历的两种方式. 注意: python2和python ...
- python中字典排序,列表中的字典排序
python中字典排序,列表中的字典排序 一.使用python模块:operator import operator #首先要导入模块operator x = {1:2, 3:4, 4:3, 2:1, ...
- Python中sorted(iterable, /, *, key=None, reverse=False)的参数中的斜杆是什么意思?
通过help(sorted)查看sorted的帮助文档,显示如下: Help on built-in function sorted in module builtins: sorted(iterab ...
- 字典的key都可以是什么
一个对象能不能作为字典的key,就取决于其有没有__hash__方法.所以所有python自带类型中,除了list.dict.set和内部至少带有上述三种类型之一的tuple之外,其余的对象都能当ke ...
- Python中字典的详细用法
#字典 #字典是Python中唯一内建的映射类型.字典中没有特殊的顺序,但都是存储在一个特定的键(key)下面,键可以是数字,字符串,甚至是元组 #一.字典的使用 #在某些情况下,字典比列表更加适用: ...
随机推荐
- C++ 自定义结构体的Priority Queue
比较函数return true 意味着排序需要交换. #include <iostream> #include <queue> #include <vector> ...
- css水平垂直居中块整理
1.绝对定位+负margin 兼容性很好,但需要指定子块的高度和宽度,以及负margin .wp{ position: relative; width: 200px; height: 200px; b ...
- win10安装CAD后出现致命错误
现在很多朋友在使用win10系统了,在win10系统打开cad却提示致命错误,这个时候应该怎么办呢?我们可以打开注册表编辑器然后找到某个注册表把数值改为0就可以解决这个问题了哦,下面就和小编一起来看看 ...
- rpm包的安装方法
每次都记不住,这次必须整理一下!cd到你存放rpm文件包的目录rpm -ivh linuxqq-v1.0.2-beta1.i386.rpm安装时可能会遇到缺少包的错误.rpm -ivh --repla ...
- (转)UVM挑战及概述
UVM的调度也具有其独特的挑战,尤其是在调试的领域.其中的一些挑战如下: 1. Phase的管理:objections and synchronization 2. 线程调试 3. Tracing i ...
- POJ 2831 Can We Build This One?
Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 1728 Accepted: 643 Case Time Limit: 2 ...
- xcode或者mac自带颜色器选择rgb格式
解决方法
- centos7设置sshd端口,firewall,selinux设置
https://blog.csdn.net/qq_31927797/article/details/81095829 #停止firewallsystemctl stop firewalld.servi ...
- servlet上传多个文件(乱码解决)
首先,建议将编码设置为GB2312,并在WEB-INF\lib里导入:commons-fileupload-1.3.jar和commons-io-2.4.jar, 可百度下下载,然后你编码完成后,上传 ...
- Swift在1.2版本的变化
从Xcode 6.3 Beta Release Notes看出,Xcode 6.3 Beta包含了很多颇为值得开发者期待的改变,共计50多处改动,同时修改了Objective-C的语法,足见苹果对Sw ...