大家好,我是正在实战各种AI项目的程序员晚枫。
先问你一个问题:你的通讯录,是按名字排序存 Excel 方便查找,还是按手机号存微信方便查找?
相信大家都会选微信——因为你知道名字,一搜就到,不用从头翻到尾。
在Python里,这个"搜名字就到"的机制,就叫字典(dict) 。
很多人学Python时,觉得字典就是"键值对存储",用列表也能实现。但等你真正用过字典做查询之后,你就会明白——为什么我说字典是Python中最被低估的数据结构。
不信?我们往下看。
为什么字典这么快? 先来看一个真实的场景。
场景:10000个用户,怎么找到指定用户? 你有一个用户列表,现在要找到 ID 为 9999 的那个用户:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 users = [{'id' : i, 'name' : f'用户{i} ' } for i in range (10000 )] def find_user_list (user_id ): for user in users: if user['id' ] == user_id: return user return None import timestart = time.time() result = find_user_list(9999 ) print (f"列表查找耗时:{time.time() - start:.6 f} 秒" )print (f"找到:{result} " )
1 2 3 4 5 6 7 8 9 10 users_dict = {user['id' ]: user for user in users} def find_user_dict (user_id ): return users_dict.get(user_id) start = time.time() result = find_user_dict(9999 ) print (f"字典查找耗时:{time.time() - start:.6 f} 秒" )print (f"找到:{result} " )
我的电脑实测结果:
列表查找:约 0.0008 秒(平均要遍历5000次) 字典查找:约 0.000002 秒(一步到位) 差了 400倍 !
字典为什么这么快? 字典底层用的是哈希表(Hash Table) ,这玩意儿有多牛呢?
想象你要在10000本书里找一本书:
查找方式 操作步骤 时间复杂度 列表(逐个找) 从第一本翻到第10000本 O(n) — 线性查找 字典(哈希表) 知道书名→直接走到对应书架 O(1) — 常数级
数据量越大,差距越明显:
100条数据:字典快 10倍 1000条数据:字典快 100倍 10000条数据:字典快 1000倍 100000条数据:字典快 10000倍 这就是为什么大厂面试必问字典——你不懂哈希表,就不懂性能优化。
字典基础:创建与访问 先从最基础的开始,确保你地基扎实。
3种方式创建字典 1 2 3 4 5 6 7 8 9 10 11 12 person = {'name' : '张三' , 'age' : 25 , 'city' : '重庆' } print (person)person2 = dict (name='李四' , age=30 , city='成都' ) print (person2)pairs = [('name' , '王五' ), ('age' , 28 ), ('city' , '北京' )] person3 = dict (pairs) print (person3)
访问值:方括号 vs get方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 scores = {'Alice' : 85 , 'Bob' : 90 , 'Charlie' : 78 } print (scores.get('David' )) print (scores.get('David' , 0 )) print (scores.get('Alice' , 0 )) keys = ['Alice' , 'David' , 'Bob' ] values = [scores.get(k, 0 ) for k in keys] print (values)
增删改查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 info = {} info['name' ] = '程序员晚枫' info['age' ] = 18 print (info)info['age' ] = 28 print (info)print (info['name' ]) print (info.get('name' )) print (info.get('gender' , '未知' )) del info['age' ] print (info) age = info.pop('name' ) print (age) print (info) info.clear() print (info)
字典进阶:6个必须掌握的技巧 技巧1:setdefault —— 一键初始化(防报错神器) 场景: 你要统计每个单词出现的次数,如果单词第一次出现,你要先初始化为0:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 words = ['apple' , 'banana' , 'apple' , 'cherry' , 'banana' , 'apple' ] counts = {} for word in words: counts[word] += 1 counts = {} for word in words: counts.setdefault(word, 0 ) counts[word] += 1 print (counts)
更简洁的写法:
1 2 3 4 words = ['apple' , 'banana' , 'apple' , 'cherry' , 'banana' , 'apple' ] counts = {word: words.count(word) for word in set (words)} print (counts)
技巧2:update —— 合并字典(别再用循环了) 1 2 3 4 5 6 7 8 9 10 11 12 dict1 = {'a' : 1 , 'b' : 2 , 'c' : 3 } dict2 = {'b' : 20 , 'd' : 4 , 'e' : 5 } dict1.update(dict2) print (dict1) dict3 = {'a' : 1 , 'b' : 2 } dict4 = {'b' : 20 , 'c' : 3 } merged = dict3 | dict4 print (merged)
技巧3:字典推导式 —— 一行构建复杂字典 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 names = ['Alice' , 'Bob' , 'Charlie' , 'David' ] name_lengths = {name: len (name) for name in names} print (name_lengths)students = {'Alice' : 85 , 'Bob' : 72 , 'Charlie' : 90 , 'David' : 68 } passing = {name: score for name, score in students.items() if score >= 80 } print (passing) keys = ['name' , 'age' , 'city' ] values = ['张三' , 28 , '重庆' ] person = dict (zip (keys, values)) print (person) matrix = {(i, j): i * j for i in range (1 , 4 ) for j in range (1 , 4 )} print (matrix)
技巧4:Counter —— 统计频率的瑞士军刀 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from collections import Counterwords = ['apple' , 'banana' , 'apple' , 'cherry' , 'banana' , 'apple' , 'apple' , 'banana' ] counts = Counter(words) print (counts)print (counts.most_common(2 )) text = "hello world" char_counts = Counter(text) print (char_counts.most_common(3 )) counter1 = Counter(['apple' , 'banana' , 'apple' ]) counter2 = Counter(['apple' , 'cherry' ]) print (counter1 + counter2)
技巧5:defaultdict —— 不用判断键是否存在 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from collections import defaultdictwords = ['apple' , 'banana' , 'apple' , 'cherry' ] counts = {} for word in words: if word not in counts: counts[word] = 0 counts[word] += 1 print (counts)counts_dd = defaultdict(int ) for word in words: counts_dd[word] += 1 print (dict (counts_dd)) animals = ['狗' , '猫' , '狗' , '兔子' , '猫' , '狗' ] by_type = defaultdict(list ) for animal in animals: by_type[animal].append(animal) print (dict (by_type))
技巧6:字典视图 —— 高效遍历键值对 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 person = {'name' : '张三' , 'age' : 28 , 'city' : '重庆' , 'job' : '程序员' } for key in person.keys(): print (key) for value in person.values(): print (value) for key, value in person.items(): print (f"{key} : {value} " ) value_to_key = {v: k for k, v in person.items()} print (value_to_key)
字典的深水区:性能与原理 字典的性能真相 字典为什么这么快?来一个硬核测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import timefor n in [100 , 1000 , 10000 , 100000 ]: data_list = list (range (n)) data_dict = {x: x for x in data_list} target = n - 1 start = time.time() for _ in range (10000 ): _ = target in data_list list_time = time.time() - start start = time.time() for _ in range (10000 ): _ = target in data_dict dict_time = time.time() - start ratio = list_time / dict_time if dict_time > 0 else 0 print (f"n={n:>6 } : 列表{list_time:.4 f} 秒 | 字典{dict_time:.6 f} 秒 | 字典快{ratio:.0 f} 倍" )
实测数据参考:
1 2 3 4 n= 100: 列表0.0021秒 | 字典0.000234秒 | 字典快9倍 n= 1000: 列表0.0189秒 | 字典0.000256秒 | 字典快74倍 n= 10000: 列表0.1876秒 | 字典0.000298秒 | 字典快630倍 n=100000: 列表1.9234秒 | 字典0.000412秒 | 字典快4668倍
结论:字典的性能优势,随着数据量增大而 指数级扩大!
字典的内存占用 字典虽然快,但内存开销也大。要有取舍意识:
1 2 3 4 5 6 7 8 import sysdata_list = list (range (1000 )) data_dict = {i: i for i in range (1000 )} print (f"列表内存:{sys.getsizeof(data_list)} 字节" ) print (f"字典内存:{sys.getsizeof(data_dict)} 字节" )
内存对比:字典比列表多占约4-5倍空间。 这就是为什么字典不适合存海量简单数据。
避坑指南:字典最容易踩的6个坑 坑1:字典的key不能是可变对象 1 2 3 4 5 6 7 8 9 10 11 12 13 d = {(1 , 2 ): 'value' } print (d[(1 , 2 )]) nested = {'level1' : {'level2' : 'value' }} print (nested['level1' ]['level2' ])
坑2:字典的.pop()和del[]的区别 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 d = {'a' : 1 , 'b' : 2 , 'c' : 3 } value = d.pop('a' ) print (f"删除了:{value} ,剩下:{d} " ) value = d.pop('x' , '不存在' ) print (value) del d['b' ]print (d)
坑3:字典推导式中的坑——小心覆盖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 data = {'a' : 1 , 'b' : 2 , 'c' : 3 } doubled = {k: data[k] * 2 for k in data} doubled = {k: v * 2 for k, v in data.items()} print (doubled) d1 = {'a' : 1 } d2 = {'a' : 2 , 'b' : 3 } merged = {**d1, **d2} print (merged)
坑4:字典是无序的(除非Python 3.7+) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 d = {} d['z' ] = 1 d['a' ] = 2 d['m' ] = 3 print (list (d.keys())) from collections import OrderedDictod = OrderedDict() od['z' ] = 1 od['a' ] = 2 od['m' ] = 3 print (list (od.keys()))
坑5:不要在遍历字典时修改它 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 d = {'a' : 1 , 'b' : 2 , 'c' : 3 } for key in list (d.keys()): if d[key] < 2 : del d[key] print (d) d = {'a' : 1 , 'b' : 2 , 'c' : 3 } keys_to_remove = [k for k, v in d.items() if v < 2 ] for key in keys_to_remove: d.pop(key) print (d)
坑6:get()的默认值只对缺失的键生效 1 2 3 4 5 6 7 8 scores = {'Alice' : 0 , 'Bob' : 90 } print (scores.get('Alice' , '缺考' )) print (scores.get('Charlie' , '缺考' )) print ('Alice' in scores) print (scores['Alice' ] == 0 )
实战案例:用字典重构用户管理系统 场景描述 做一个用户查询系统,有以下需求:
根据用户ID快速查询用户信息 统计每个城市的用户数量 找出年龄最大的用户 按城市分组用户列表 ❌ 低效做法(用列表) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 users_list = [ {'id' : i, 'name' : f'用户{i} ' , 'age' : 18 + i % 50 , 'city' : ['重庆' , '成都' , '北京' , '上海' ][i % 4 ]} for i in range (10000 ) ] def find_user (user_id ): for user in users_list: if user['id' ] == user_id: return user return None def count_by_city (): counts = {} for user in users_list: city = user['city' ] counts[city] = counts.get(city, 0 ) + 1 return counts def find_oldest (): return max (users_list, key=lambda u: u['age' ]) import timet1 = time.time() for _ in range (1000 ): find_user(9999 ) print (f"查1000次用户:{time.time()-t1:.4 f} 秒" )t2 = time.time() count_by_city() print (f"统计城市用户:{time.time()-t2:.6 f} 秒" )t3 = time.time() find_oldest() print (f"找最大年龄:{time.time()-t3:.6 f} 秒" )
✅ 高效做法(用字典) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 users_dict = { user['id' ]: user for user in users_list } def find_user (user_id ): return users_dict.get(user_id) def count_by_city (): counts = {} for user in users_dict.values(): city = user['city' ] counts[city] = counts.get(city, 0 ) + 1 return counts def find_oldest (): return max (users_dict.values(), key=lambda u: u['age' ]) from collections import defaultdictdef group_by_city (): groups = defaultdict(list ) for user in users_dict.values(): groups[user['city' ]].append(user) return dict (groups) t1 = time.time() for _ in range (1000 ): find_user(9999 ) print (f"查1000次用户:{time.time()-t1:.4 f} 秒" )t2 = time.time() count_by_city() print (f"统计城市用户:{time.time()-t2:.6 f} 秒" )t3 = time.time() find_oldest() print (f"找最大年龄:{time.time()-t3:.6 f} 秒" )
性能对比:
1 2 3 需求1-查用户(1000次):列表0.42秒 → 字典0.0008秒,快了525倍! 需求2-统计城市:列表0.0031秒 → 字典0.0021秒 需求3-找最大年龄:列表0.0023秒 → 字典0.0009秒,快了2.5倍
常见面试题 Q1:字典的key可以是哪些类型?
A:必须是可哈希的(不可变) 类型,如字符串(str)、数字(int/float)、元组(tuple)。 列表(list)、字典(dict)、集合(set) 不能 做key。
1 2 3 4 5 6 7 8 d = { 'name' : '张三' , 1 : '整数' , (1 , 2 ): '元组' , }
Q2:Python 3.9+ 两个字典怎么合并?
A:3种方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 d1 = {'a' : 1 , 'b' : 2 } d2 = {'b' : 20 , 'c' : 3 } merged = d1 | d2 print (merged) d1 |= d2 print (d1) d1 = {'a' : 1 , 'b' : 2 } d2 = {'b' : 20 , 'c' : 3 } d1.update(d2) print (d1)
Q3:如何保持字典有序?
A:Python 3.7+ 的字典默认有序 ,不需要额外处理。如果需要兼容旧版本,用 collections.OrderedDict。
Q4:字典和列表怎么选?
A:看你的主要操作是什么:
需要快速查找/存在判断 → 用字典(O(1) vs O(n)) 需要保持顺序 → 用列表 需要存储重复值 → 用列表 需要去重 → 用集合(比字典更节省空间) Q5:字典的底层原理是什么?
A:哈希表(Hash Table) 。Python通过哈希函数将key转换为数组下标,直接访问对应位置,时间复杂度O(1)。当发生哈希冲突时,使用开放寻址法或链地址法解决。
推荐:AI Python零基础实战营 想深入学习Python数据结构,把算法面试题全部拿下?
课程内容:
✅ Python基础语法 ✅ 数据结构详解(列表、字典、集合、元组) ✅ 算法与复杂度分析(时间/空间复杂度) ✅ 实战项目练习 🎁 限时福利 :送《Python编程从入门到实践》实体书
👉 点击了解详情
本讲小结 操作 代码 说明 创建字典 {'a': 1} 或 dict(a=1)花括号或dict函数 安全取值 d.get('key', 0)键不存在不报错 初始化 d.setdefault('k', 0)防KeyError 合并字典 d1.update(d2)d2覆盖d1 推导式 {k:v for k,v in d.items()}一行构建字典 统计频率 Counter(data)快速计数 默认字典 defaultdict(int)自动初始化
💡 记住一句话 :要快查、用dict;要存唯一、用set;要存顺序、用list。三个数据结构,各有分工!
相关阅读 PS:字典是Python最高效的数据结构之一。面试必问,工作必用——掌握它,你的代码性能和可读性都会大幅提升。
📚 推荐教材 主教材 :《Python 编程从入门到实践(第 3 版)》
📚 推荐:Python 零基础实战营 系统学习Python,推荐这个免费入门课程 👇
特点 说明 🎯 专为0基础设计 门槛低,上手快 📹 配套视频讲解 配合文章学习效果更好 💬 专属答疑群 遇到问题有人带 🎁 实体书赠送 优秀学员送《Python编程从入门到实践》
👉 点击免费领取 Python 零基础实战营
💬 联系我 主营业务 :AI 编程培训、企业内训、技术咨询
🎓 AI 编程实战课程 想系统学习 AI 编程?程序员晚枫的 AI 编程实战课 帮你从零上手!