问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

什么样的类型可以作为python字典的key

发布网友 发布时间:2022-05-10 04:38

我来回答

2个回答

懂视网 时间:2022-05-10 08:59

很多Python初学者经常会有这样的疑问,为什么Python有tuple(元组)和list(列表)两种类型?为什么tuple可以作为字典的key,list不可以?要理解这个问题,首先要明白python的字典工作原理。

20180904102251_724.png

1.Python的字典是如何工作的

在Python中,字典也就是一个个的“映射”,将key映射到value:

# 对一个特定的key可以得到一个value

value = d[key]

为了实现这个功能,Python必须能够做到,给出一个key,找到哪一个value与这个key对应。先来考虑一种比较简单的实现,将所有的key-value键值对存放到一个list中,每当需要的时候,就去遍历这个list,用key去和键值对的key匹配,如果相等,就拿到value。但是这种实现在数据量很大的时候就变得很低效。它的算法复杂度是O(n),n是存放键值对的数量。(关于Hash表具体的工作原理,可以参考我的这篇文章。

为此,Python使用了hash(哈希)的方法来实现,要求每一个存放到字典中的对象都要实现hash函数,这个函数可以产生一个int值,叫做hash value(哈希值),通过这个int值,就可以快速确定对象在字典中的位置。然而,由于Hash碰撞的存在,可能存在两个对象的Hash值是相同的,所以查找字典的过程中,要比较hash值,还要比较value的值。

这个查询的大致过程如下:

def lookup(d, key):

字典的查询过程概括为下面3步:

1. 通过hash函数将key计算为哈希值.

2. 通过hash值确定一个位置,这个位置是一个存放着

可能存在冲突的元素的数组(很多地方叫做“桶”,bucket),

每一个元素都是一个键值对,理想情况下,这个数组里只有1个元素.

3. 遍历这个数组,找到目标key,返回对应的value.

h = hash(key)   # step 1
 cl = d.data[h]   # step 2
 for pair in cl:  # step 3
 if key == pair[0]:
  return pair[1]
 else:
 raise KeyError, "Key %s not found." % key

要使这个查找过程正常工作,hash函数必须满足条件:如果两个key产生了不同的hash value,那么这两个key对象是不相等的。即

for all i1, i2, if hash(i1) != hash(i2), then i1 != i2

否则的话,hash value不同,对象却相同,那么相同的对象产生不同的hash value,查找的时候就会进错桶(step 2),在错误的桶里永远也找不到你要找的value。

另外,要让字典保持高查找效率,还要保证:当两个key产生相同的hash value,那么他们是相等的。

for all i1, i2, if hash(i1) == hash(i2), then i1 == i2

这样做的目的是,尽量满足每个hash桶只有一个元素。为什么要这样呢? 考虑下面这个hash函数。

def hash(obj):

return 1

这个hash函数是满足上面我们谈的第一个条件的:如果两个key的hash value不同,那么两个key对象不相同。因为所有的对象产生的hash value都是1,所以不存在能产生不同hash value的key,也就不存在不满足的情况。但是这样做的坏处是,因为所有的hash value都相同,所以就把所有的对象分到了同一个地方。查找的时候,进行到第三步,遍历的效率就变成了O(n).

Hash函数应该保证所有的元素平均的分配到每一个桶中,理想的情况是,每一个位置只有一个元素。

以上两个原则,第一个保证了你能从字典中拿到要找的元素,第二个保证了查询效率。


2.字典Key要满足的要求

经过上面的讨论,我们应该明白Python为什么对字典的key有这样的要求了:

要作为字典的key,对象必须要支持hash函数(即__hash__),相等比较(__eq__或__cmp__),并且满足上面我们讨论过的条件。

3.List为什么不能作为key

至于这个问题,最直接的答案就是:list没有支持__hash__方法,那么为什么呢?

对于list的hash函数,我们可能有下面两种实现的方式:

第一种,基于id。这满足条件——“如果hash值不同,那么他们的id当然不同”。但考虑到list一般是作为容器,基于id来hash可能会导致下面两种情况:

用相同的list作为key去字典中找某个元素可能会得到不同的结果,因为是基于id hash的,所以即使他们的内容相同,字典依然将他们作为不同的元素对待。创建一个一模一样的list用字典查找永远会得到一个KeyError。

第二种,基于内容。tuple就是这样做的,但是要注意一点,tuple是不可以修改的,但list是可以修改的。当list修改之后,你就永远别想再从字典中拿回来了。见下面的代码。

>>> l = [1, 2]
>>> d = {}
>>> d[l] = 42
>>> l.append(3)
>>> d[l] # 原来的hash值是基于[1, 2]hash的,
  # 现在是基于[1, 2, 3],所以找不到
Traceback (most recent call last):
 File "<interactive input>", line 1, in ?
KeyError: [1, 2, 3]
>>> d[[1, 2]] # 基于hash [1, 2]
  # 但是遍历的时候找不到key相等的键值对
  #(因为字典里的key变成了[1, 2, 3]
Traceback (most recent call last):
 File "<interactive input>", line 1, in ?
KeyError: [1, 2]

鉴于两种实现的方式都存在一定的副作用,所以Python规定:

内置的list不能作为字典的key.

但tuple是不可变,所以tuple可以作为字典的key。

(2018年1月2日更新,上面我说tuple不可变可以作为字典的key,这句话并不是完全正确的。tuple只是相对不可改变的,如果tuple中有元素是可变对象,那么虽然tuple不可改变,那么其中元素所指向的对象是可变的,所以同样会出现上面“list不能作为字典的key”这个问题,即含有可变对象的tuple也不能作为字典的key,举个例子就很好懂了。)

In [11]: li = [1,2,] 
In [12]: d = dict() 
In [13]: t2 = (1,2,)
In [14]: t3 = (1,2,li,) 
In [15]: d[li] = 1
---------------------------------------------------------------------------
TypeError     Traceback (most recent call last)
<ipython-input-15-cc334e53316a> in <module>()
----> 1 d[li] = 1
 
TypeError: unhashable type: 'list'
 
In [16]: d[t2] = 2
 
In [17]: d[t3] = 3
---------------------------------------------------------------------------
TypeError     Traceback (most recent call last)
<ipython-input-17-c9021fe91ba8> in <module>()
----> 1 d[t3] = 3
 
TypeError: unhashable type: 'list'

4.自定义的类型作为字典的Key

用户自定义的类型就可以作为key了,默认的hash(object)是 id(object), 默认的cmp(object1, object2)是cmp(id(object1), id(object2)),同样是可以修改的对象,为什么这里就没有上面说的问题呢?

一般来说,在映射中比较常见的需求是用一个object替换掉原来的,所以id比内容更重要,就可以基于id来hash如果内容重要的话,自定义的类型可以通过覆盖__hash__函数和__cmp__函数或__eq__函数来实现

总结

值得注意的是:将对象和一个value关联起来,更好的做法是将value设置为对象的一个属性。

热心网友 时间:2022-05-10 06:07

可变类型包括dict,list都不可以作为字典的key,而原子类型以及tuple则可以。追答key:一是不能重复出现;二是键不可变。

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
武汉大学在职研究生考试科目有哪些 报考武汉大学在职研究生录取率高吗? 武汉大学mpa在职研究生好考吗 忘记车子买哪家保险 怎么查 67年的羊和什么生肖最配夫妻 67年的羊和69年鸡相配吗? 67年羊男与69年鸡女同是11月出生婚配如何? 67年9月和69年十月的结婚 “新菊亦侵篱”的出处是哪里 “闲绕篱头看菊花”的出处是哪里 笔记本电脑怎么看自己是家庭版还是还是办公版 笔记本电脑,在哪里可以看见配置,和版本信息, 我的XBOX360 GTA5进不去游戏 求各位大神们破解xbox360怎么玩GTA5OL xbox360上的GTA5怎么打不开 Xbox360玩GTA5时,玩不了,有图。帮忙看看是怎么一回事 xbox360玩gta5出现如下问题 我该怎么解决 我最近安装了gta5不义之财dlc以後 我的xbox360每次玩gta5一段时间就会黑屏了 只能退 xbox360 双破 我玩gta5为什么 每次刚做完一个任务 退出后 又要重玩 而且每到任 gta5xbox360版怎么退出 弱弱的问一下,xbox360的GTA5怎么没有退出游戏的按键啊 弱弱的问一下。xbox360的GTA5怎么没有退出游戏的按键啊。 中国海洋大学大气科学类专业毕业后做什么工作?谢谢 中国海洋大学毕业分配工作主要在什么地方 上海海洋大学毕业后一般到什么部门工作? 中国海洋大学毕业后到哪里工作,工资有多少 上海海事大学、大连海事大学毕业后主要从事什么工作? 上海海洋大学就业怎么样?哪些专业好找工作啊? 中国海洋大学难不难考?毕业后能找到什么工作? 山东大学海洋资源开发技术专业毕业生就业方向有什么? 如何评价陈可辛为苹果拍摄的短片《三分钟》? 如何评价苹果广告片《三分钟》的促销方法 评价康有为三分钟演讲 DNF:玩家3分钟爆2神话,群友祝他只爆神话,如何评价? 如何评价电视剧《猎场》三分钟片花? 如何评价哪些三分钟热度的人? 怎么评价对感情三分钟热度的人? 怎样评价一个三分钟热度的人 那个 谁知道约德尔人有多高 做梦梦见自己往最高的地方爬,上去以后有一个坟墓,是什么意思? 约德尔人领主是谁 安妮和约尔德人比身高哪个高一点? lol诺克萨斯之手身高多少? lol诺克萨斯之手身高多少? 斗鱼电竞约德尔人多大 约德尔人的维迦 约德尔人的维迦 英雄联盟中那些英雄是约德尔人 梦见最爱的人死了意味着什么? 梦到自己最爱的、已逝的人是怎样的感受?