Python 字典:你每天都会用到的”通讯录”
写 Python 代码,字典(Dictionary)是我用得最多的数据结构,没有之一。
它就是一个通讯录:你记一个人的名字(键),就能找到他的电话号码(值)。存什么都能快速找到,这就是字典的价值。
字典长什么样?怎么建?
字典用大括号 {},里面是”键:值”的配对,逗号隔开。
1 | # 基本字典(键为字符串,值为整数) |
两个规则记住就行:
- 键必须是不可变的。字符串、数字、元组都可以,列表不行。因为列表能变,Python 没法保证它”始终能代表同一个东西”。
- 键不能重复。你如果写了两次同一个键,后面的会覆盖前面的。
增删改查,字典的”四件套”
查:用 [] 或者 get()
1 | student = {'name': 'Alice', 'age': 20} |
[] 和 get() 的区别:[] 找不到就报错,程序崩溃;get() 找不到返回默认值,程序还能继续跑。不确定键存不存在的时候,用 get() 更安全。
改/增:直接赋值
1 | student = {'name': 'Alice', 'age': 20} |
Python 不管你是改还是增,反正 字典[键] = 值 这一句就把键值对放进去了。存在就更新,不存在就新增。
删:del、pop、clear
del 字典名[键]:删除指定键值对。字典名.pop(键):删除并返回指定键的值。字典名.clear():清空字典所有键值对。
1 | student = {'name': 'Alice', 'age': 21, 'major': 'Math'} |
pop 比 del 多一个功能——它把删掉的值返回给你,有时候有用。
遍历字典:取出里面的数据
遍历键
1 | fruit_prices = {'apple': 5, 'banana': 3, 'orange': 4} |
遍历值
1 | fruit_prices = {'apple': 5, 'banana': 3, 'orange': 4} |
遍历键值对(最常用)
1 | fruit_prices = {'apple': 5, 'banana': 3, 'orange': 4} |
几个实战场景,看看字典怎么用
场景1:统计词频
这是字典最经典的应用,没有之一。
1 | text = "hello world hello python hello world" |
get(word, 0) 的意思:如果 word 已经在字典里,返回它的值;如果不在,返回 0。然后加 1 赋值回去。一行搞定统计,比用 if-else 简洁多了。
场景2:按类别分组
1 | users = [ |
setdefault 可以简化这段代码:
1 | city_users.setdefault(city, []).append({'name': name, 'age': age}) |
setdefault 的意思是:如果 city 存在,返回它的值;如果不存在,用第二个参数创建默认值并返回。一行代替 if-else 判断。
场景3:用字典替代多个 if-elif
写代码的时候经常遇到一堆 if-elif,看着烦。可以用字典把条件和操作映射起来:
1 | def add(x, y): return x + y |
这个技巧在处理命令分发、路由选择的时候很好用,代码比一堆 if-elif 干净多了。
场景4:缓存计算结果(性能优化)
计算斐波那契数列的时候,不用缓存会重复算很多次。用字典把算过的结果记下来:
1 | cache = {} |
没缓存的时候算 fib(40) 可能要 30 秒,有缓存 0.001 秒。差别巨大。
几个容易踩的坑
坑1:遍历的时候删东西
1 | d = {'a': 1, 'b': 2, 'c': 3} |
正确做法:遍历字典的副本(转换成列表再遍历):
1 | for key in list(d.keys()): |
坑2:浅拷贝只复制了外层
1 | original = {'list': [1, 2, 3]} |
dict.copy() 是浅拷贝,里面的列表还是同一个引用。想完全独立用 copy.deepcopy()。
坑3:可变对象当默认参数
1 | def add_user(name, user_dict={}): |
默认参数在函数定义的时候就创建了,每次调用用的是同一个字典对象。正确做法是用 None 占位:
1 | def add_user(name, user_dict=None): |