Python 字典和集合,原来底层是这么玩的

昨天写 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、数字、元组):调用方法只会返回新对象,原数据永远不变

写在最后

这次从一个报错啃完所有知识点,脑子里就刻下了三个核心:

  1. 字典底层是哈希表,靠 key 哈希定位 value,key 必须是不可变类型
  2. 可变 / 不可变对象是核心,别再搞混sortreplace的效果
  3. Set 就是精简版字典,专门用来去重、做集合运算

也别神话字典,小数据量、不需要快速查找的场景,用列表更省内存。

这次踩坑爬坑的过程还挺有意思的,你们平时用字典还遇到过啥奇葩问题?评论区一起聊聊~

相关推荐
星卯教育tony1 小时前
CIE中国电子学会2026年3月c++ Python scratch 机器人真题试卷含参考答案
c++·python·scratch·电子学会
linksinke1 小时前
在 CentOS 7.x 外网环境离线构建便携式 Python 3.11.4 的方案参考
linux·python·centos
wapicn991 小时前
API接口调试笔记:从注册到第一个数据返回,全流程详解
java·开发语言·python·lua
logo_281 小时前
python指定目录进行虚拟环境配置
python·虚拟环境
大数据魔法师1 小时前
Streamlit(十七)- API 参考文档(十)- 身份认证与用户信息组件
python·web
geovindu1 小时前
python: Bounded Parallelism Pattern
开发语言·python·设计模式·有界并行模式
大明者省2 小时前
Ubuntu Python 部署终极版教程
开发语言·python·ubuntu
KANGBboy2 小时前
java知识二(数组)
java·开发语言·python
Cloud_Shy6182 小时前
解读《Effective Python 3rd Edition》:从练气到老魔(第一章 Item 4 - 6)
android·数据库·论文阅读·python