昨天写 Python 小脚本,脑子一抽想把列表当成字典的 key 存数据,敲完回车控制台直接甩来一个报错,当时我人都傻了,盯着屏幕看了半天,愣是没明白为啥字符串能当 key,列表就不行。

截图就是当时控制台报的 unhashable type: 'list',就是这行破代码,逼得我把字典、哈希表、可变不可变对象全啃了一遍。
说实话,我之前对字典的理解就停留在 "存键值对的容器",啥底层原理、使用限制一概不知,这次踩坑后才算真的吃透,趁热乎把我的思考过程分享给大家。
先搞懂:字典为啥比列表快到飞起?
最开始我存「名字 - 成绩」,用的是最笨的原始方法:
python
# 两个列表,下标一一对应,纯纯笨方法
names = ['周杰伦', '蔡徐坤', '周星驰']
score = [100, 2.5, 100]
# 想查蔡徐坤的分数?得遍历列表找下标,数据多了直接慢死
这就是列表的硬伤:查找、插入的速度会跟着元素变多越来越慢。
然后 Python 的字典就登场了,我给它打个最贴切的比方:字典就是书本最前面的索引目录 。你想找某一页内容,不用从头翻到尾,看目录直接定位页码;字典底层是哈希表,key 通过哈希算法算出一个唯一的索引,直接找到 value 的存储地址,所以查找速度是 O (1),不管多少数据,都是秒查。
这才是字典的正确打开方式:
python
# 字python典:key-value键值对
d = {'周杰伦': 100, '蔡徐坤': 2.5, '周星驰': 100}
# 直接查找,瞬间出结果
print(d['蔡徐坤']) # 2.5
日常用字典的增删改查,我也把踩过的小坑整理好了:
python
# 1. 修改已有的key
d['周杰伦'] = 88
print(d['周杰伦']) # 88
# 2. 千万别直接访问不存在的key!会直接报错
# d['ikun'] # KeyError,血的教训
# 3. 安全访问:用in判断 / get方法
print('ikun' in d) # False
print(d.get('ikun')) # None,不报错
print(d.get('ikun', -1)) # 可以指定默认值,太香了
print(d.get('蔡徐坤', -1)) # 2.5
# 4. 删除key
d.pop('蔡徐坤')
核心大坑:为啥列表绝对不能当字典的 key?
绕回最开始的报错,我第一反应是:"Python 歧视列表?凭啥字符串就行?"后来翻了哈希表的设计规则,才恍然大悟:字典的 key 必须是可哈希的,也就是不可变类型。
哈希算法的硬性要求:key 必须固定不变!如果 key 是可变的(比如列表),你改一下列表内容,哈希计算的结果就变了,字典直接找不到 value 的位置,整个哈希表就乱套了。
字符串、数字、元组是不可变的,所以能当 key;列表、字典是可变的,Python 直接禁止它们当字典的 key。

顺便记一下字典的优缺点,别把它当万能药:
- 优点:查找、插入极快,不随元素增多变慢
- 缺点:占用内存大,典型的空间换时间
秒懂 Set:就是个没有 value 的字典
搞懂字典后,看 Set 集合简直是降维打击!Set 和字典的底层原理一模一样,唯一区别:Set 只存 key,不存 value,而且 key 不能重复,天生自带去重功能。
直接上可运行的 demo:
python
# 两种创建set的方式
s = {1, 2, 3}
# 列表转set,自动去重
s = set([1,2,3,2,5])
print(s) # {1,2,3,5}
# 增删元素
s.add(4)
s.remove(4)
# 集合运算:交集、并集,处理数据超好用
s1 = {1, 2, 3}
s2 = {2, 3, 4}
print(s1 & s2) # 交集 {2,3}
print(s1 | s2) # 并集 {1,2,3,4}
终极迷惑:可变对象 vs 不可变对象
本来以为字典搞定就收工了,结果又被列表sort和字符串replace坑了一把,这俩的效果完全不一样!
python
# 列表:可变对象
a = ['c', 'b', 'a']
print(a.sort()) # None!坑!sort直接修改原列表,不返回新对象
print(a) # ['a', 'b', 'c']
# 字符串:不可变对象
str_obj = 'abc'
# replace不会改原字符串,只会返回新的字符串!
print(str_obj.replace('a', 'A')) # Abc
print(str_obj) # abc 原对象纹丝不动
现在彻底理清了:
- 可变对象(list、dict、set):调用方法会直接修改自身
- 不可变对象(str、数字、元组):调用方法只会返回新对象,原数据永远不变
写在最后
这次从一个报错啃完所有知识点,脑子里就刻下了三个核心:
- 字典底层是哈希表,靠 key 哈希定位 value,key 必须是不可变类型
- 可变 / 不可变对象是核心,别再搞混
sort和replace的效果 - Set 就是精简版字典,专门用来去重、做集合运算
也别神话字典,小数据量、不需要快速查找的场景,用列表更省内存。
这次踩坑爬坑的过程还挺有意思的,你们平时用字典还遇到过啥奇葩问题?评论区一起聊聊~