我刚开始学编程那会儿,就觉得字典这玩意儿挺好用的,存东西嘛键值对,多方便。那时候没多想,随手就拿数字,字符串当键使,好使,没毛病。比如存个学生分数,学号当键,分数当值,一下就取出来了,简单直接。
后来随着写的代码越来越多,手也欠了点,就想着,诶,列表(list)也能装东西,那我能不能拿列表当键?我记得当时就信心满满地写了行代码,想把一个列表当字典的键塞进去。结果,啪!报错了。
那错误提示,啥TypeError: unhashable type: ‘list’,当时看得我一头雾水。啥叫‘unhashable’?这词儿以前哪听过。字典不就是个容器吗,我塞啥它就装啥呗,怎么还挑三拣四的了?
当时就懵了,赶紧去翻资料。这一翻,才知道里面门道还挺多的。原来字典的键,是有点讲究的,它不是你想用啥就用啥的。
什么是“可哈希”?
简单点说,就是字典为了能快速地找到你想要的值,它得给每个键算个“指纹”,这个“指纹”就叫哈希值。每次你拿键去字典里找东西,它就先把你的键算个指纹,然后根据这个指纹,飞快地定位到对应的位置。这个“指纹”就得是个稳定的东西,不能说你塞进去的时候是A指纹,下次来找的时候,这个键自己变了,变成了B指纹,那字典可就找不着了。
一个东西要能当字典的键,它就必须满足两个条件:
小编温馨提醒:本站只提供游戏介绍,下载游戏请前往89游戏主站,89游戏提供真人恋爱/绅士游戏/3A单机游戏大全,点我立即前往》》》绅士游戏下载专区
- 得是“不变”的:它创建了之后,里面的内容就不能再改了。
- 得能算出“指纹”:Python能给它算出一个固定的哈希值。
满足这两个条件的,就叫“可哈希”类型。不满足的,就叫“不可哈希”类型。
哪些类型“可哈希”?
我一开始用的那些,比如数字(整数、浮点数)、字符串,它们都是“可哈希”的。为啥?因为一旦你定义了一个数字或者一个字符串,你就不能再改它里面的内容了。你把“你好”这个字符串写死了,它就永远是“你好”,不能变成“你好吗”。所以它们是“死心眼”,内容不变,指纹就固定,当然能当键。
后来我发现,除了这些,元组(tuple)也能当键。这又是一个小小的惊喜,因为元组看起来跟列表挺像的,不都是一串东西用括号包起来嘛但我很快就明白,元组和列表最大的不同就是:元组也是“不变”的。你一旦定义了一个元组,比如(1, 2, 'hello'),你就不能再往里面加东西,也不能删东西,更不能改里面的元素了。它也是个“死心眼”。
还有一些不那么常用的,比如布尔值(True/False),它们本身就是固定的,肯定也没问题。甚至是一些特殊的对象,比如空值None,也能当键。
哪些类型“不可哈希”?
反过来,那些会报错的,都是“不可哈希”的。我最初遇到的列表(list),就是典型代表。列表“太活泼”了,能随时往里加东西(append),删东西(remove),改东西(修改某个索引位置的值)。你看,它内容老变,那“指纹”还能固定吗?肯定不能,所以Python不让它当键。
同样的道理,集合(set)和字典(dict)自己也不能当键。它们也都是能随便改内容的货。你往集合里加个元素,或者往字典里塞个键值对,这内容就变了。这种“说变就变”的家伙,Python可不让它们当键。不然字典就乱套了,想找东西也找不到了。
集合有个“铁石心肠”的兄弟,叫`frozenset`。这个就好使了,因为它跟元组一样,一旦创建就冻结了,不能再改。所以我有时候需要根据一些不重复的元素组合来当键时,就会用`frozenset`,挺方便的。
至于我们自己写的那些“对象”,要用它们当键,那得看情况。默认情况下,我们自己创建的类的实例,是“不可哈希”的。但如果你非要让它们也能当键,那就得去类里面写点特殊的代码,告诉Python怎么给你的对象算“指纹”,还得告诉它啥时候两个对象是“一样”的。这有点超出入门了,但原理还是那个:得让它“可哈希”和“能比较”,不能是“变来变去”的。
刚开始觉得这东西挺绕的,不就存个数据嘛搞这么复杂干但后来写一些需要根据复杂条件做查找的程序时,才发现这个“哈希”的概念真是太重要了。比如,我有时候要根据好几个条件来唯一标识一个东西,把这几个条件打包成一个元组当键,比把它们拼成一个长长的字符串当键,要清晰得多,代码也更优雅。也能规避掉很多不必要的麻烦。
总结一句话,想把啥东西当字典的键?先想想它是不是个“老实人”,内容定下来了就不能变的,是,那八成就能用;不是,那就得绕道走了。

