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__的继承使用问题
---------------------

原文地址:https://www.cnblogs.com/zxmbky/p/10184054.html

时间: 2024-08-22 23:35:18

Python中字典的key都可以是什么的相关文章

python中字典以key排序,以value排序。以及通过value找key的方式

1.sorted函数首先介绍sorted函数,sorted(iterable,key,reverse),sorted一共有iterable,key,reverse这三个参数. 其中iterable表示可以迭代的对象,例如可以是dict.items().dict.keys()等,key是一个函数,用来选取参与比较的元素,reverse则是用来指定排序是倒序还是顺序,reverse=true则是倒序(从大到小),reverse=false则是顺序(从小到大),默认是reverse=false. 2.

Python中字典和集合

映射类型: 表示一个任意对象的集合,且可以通过另一个几乎是任意键值的集合进行索引 与序列不同,映射是无序的,通过键进行索引 任何不可变对象都可用作字典的键,如字符串.数字.元组等 包含可变对象的列表.字典和元组不能用作键 引用不存在的键会引发KeyError异常 1)字典 dict { } 空字典 { key1:value1,key2:value2,... } 字典在其它编程语言中又称作关联数组或散列表: 通过键实现元素存取:无序集合:可变类型容器,长度可变,异构,嵌套 支持的操作: len(D

python 中字典、数组

a = {"k1":"v1","A":1,"a":2,5:"i5"} a["A"] ="直接修改" a["aa"]="没有的键是新建" del a["a"] #删除一个元素 d.clear() #清空一个字典 1.字典是键值对,没有顺序, 2.键大小写敏感 3.键值可以是混合类型的 b = ["a&

Python中字典get方法的使用

Python中字典get方法的使用 说起来,这个功能是否需要学习还真是有待于讨论.其实,知道了字典这种数据结构以后,通过最基本的Python功能也能够实现一个类似的功能.不过,既然内置了这个功能那么我们就没有必要重复造车轮. 在字典内置的方法中,想说的方法为get.这个方法是通过键来获取相应的值,但是如果相应的键不存在则返回None.其实,None只是一个默认的返回值但是并不是一个不能修改的返回值.其实,如果查询失败,我们可以指定一个返回值. 上面提到的所有功能汇总在一个示范代码,具体如下: #

Python中字典的详细用法

#字典 #字典是Python中唯一内建的映射类型.字典中没有特殊的顺序,但都是存储在一个特定的键(key)下面,键可以是数字,字符串,甚至是元组 #一.字典的使用 #在某些情况下,字典比列表更加适用: #1.表示一个游戏棋盘的状态,每个键都是由坐标值组成的元组 #2.存储文件修改时间,用文件名作为键; #3.数字电话\地址薄 #1.使用列表创建一个电话本,(这里用字符串表示电话号码,以0开头的数字回会被编译成8进制数字) name=["A","B","C&

浅谈python中字典

1.字典的定义方式有以下: a=dict(one=1,two=2,three=3) b={'one':1,'two':2,'three':3} c=dict(zip(['one','two','three'],[1,2,3])) d=dict([('two',2),('one',1),('three',3)]) e=dict({'three':3,'one':1,'two':2}) 其中这四种定义方式完全等效,有一点需要牢记,标准库中所有的映射类型都是通过字典(dict)来实现,其中只有可散列(

字典的key都可以是什么

一个对象能不能作为字典的key,就取决于其有没有__hash__方法.所以所有python自带类型中,除了list.dict.set和内部至少带有上述三种类型之一的tuple之外,其余的对象都能当key. 比如数值/字符串/完全不可变的元祖/函数(内建或自定义)/类(内建或自定义)/方法/包等等你能拿出手的,不过有的实际意义不高.还有数值型要注意,因为两个不同的相等数字可以有相同的哈希值,比如1和1.0. 原文地址:https://www.cnblogs.com/zknublx/p/120543

python中字典的使用

python中的字典的特性: 在字典中的元素是散列存放的,没有顺序, 在进行增删改查的时候使用字典中需要的关键字(key)即可. 一: 创建字典 1)直接定义一个: dict = {'ob1':'computer', 'ob2':'mouse', 'ob3':'printer'} 注: 字典中可包含列表:dict={'yangrong':['23','IT'],"xiaohei":['22','dota']} 字典中可包含字典:dict={'yangrong':{"age&q

python中字典,元组,集合

python中的字典,元组,集合 -dict -tuple -set dict 字典增删改查 字典创建 my_dict = {'a':1,'b':2} my_dict {'a': 1, 'b': 2} de8ug = {'name':'de8ug', 'city':'beijing', 'code':'python'} #个人信息 de8ug['name'] 'de8ug' de8ug['city'] 'beijing' de8ug.get('name') #尝试去获取name的值 'de8ug