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)下面,键可以是数字,字符串,甚至是元组 #一.字典的使用 #在某些情况下,字典比列表更加适用: ...
随机推荐
- 转 在shell脚本中使用expect实现scp传输问题
1.安装expect expect用于shell脚本中自动交互,其是基于tcl编程语言的工具.所以安装expect首先安装tcl.本文中使用的是expect5.45和tcl8.6.6. 安装tcl [ ...
- [已读]用Angularjs开发下一代web应用
屯了很久了,貌似是国内出现的第一本讲angularjs的书...上上周看完的时候,angular2都要出来了...angular的双向绑定很赞,因为之前公司后台系统我都用tmodjs做,模板语法什么的 ...
- C#中 添加 删除 查找Xml中子节点
//添加xml节点 private void AddXml(string image, string title) { XmlDocument xmlDoc = new X ...
- SQL Server插入中文数据出现乱码问题
我在用sql server存储数据的时候发现中文全变成了问号,我知道中文是特殊的编码.所以在数据库设计的时候包含中文的字段就是nvarchar,但是还是成了问号 好了,不多说了,解决方案如下: 在存储 ...
- java实现打开Windows控制台窗口
在写Python程序的时候突发奇想了一下,能不能用java代码实现打开控制台窗口呢? 经过查询网络资料和java API文档,终于实现了: package com.primeton.cmd; impo ...
- Android程序初体验
第一个程序的实现的最终功能是: 点击"正确"或者"错误"会得到一个是否正确的提示. 直接上效果图. 此次涉及代码编写的文件有4个: package co ...
- webpack前端构建工具学习总结(三)之webpack.config.js配置文件
Webpack 在执行的时候,除了在命令行传入参数,还可以通过指定的配置文件来执行.默认情况下,会搜索当前目录的 webpack.config.js 文件,这个文件是一个 node.js 模块,返回一 ...
- POJ 1947 Rebuilding Roads (树形DP)
题意:给一棵树,在树中删除一些边,使得有一个连通块刚好为p个节点,问最少需要删除多少条边? 思路: 因为任一条边都可能需要被删除,独立出来的具有p个节点的连通块可能在任意一处地方.先从根开始DFS,然 ...
- FZU 1977 Pandora adventure (插头DP,常规)
题意:有一个n*m矩阵,其中有些格子必走,有些格子不可走,其他格子是可走也可不走,问有多少条哈密顿回路? 思路: 本来是一道很简单的题,代码写多了连白痴bug都查不出了,竟然用i>=ex& ...
- Openjudge 1.13-23:区间内的真素数
总时间限制: 1000ms 内存限制: 65536kB 描述 找出正整数 M 和 N 之间(N 不小于 M)的所有真素数. 真素数的定义:如果一个正整数 P 为素数,且其反序也为素数,那么 P 就 ...