资讯详情

Python 字典 (Dictionary)使用详解

📅 2026/10/12 4:03:35 | 华诺云谱 👁 阅读
Python 字典 (Dictionary)使用详解
前言前面谈字典的文章多半围绕「语法」——怎么建、怎么读、有什么方法。但真实项目里字典最常出现的地方是它解决的具体问题读配置、数次数、把一堆记录按某个字段分堆、缓存重复计算的结果、和 JSON 互相转换。这篇换一个角度专讲实战用法。选这个角度是有原因的字典的 API 其实很少真正让人卡住的往往不是「不会用get」而是「不知道该用字典」或者「用了却埋了个坑」。比如 JSON 的键只能是字符串Python 字典的键却可以是元组直接json.dumps就会报错再比如用setdefault分组比分堆写法省事但写错了会往字典里塞进空桶。本文给出五个典型场景每个都是可复制运行的完整片段以 Python 3.8 为基准涉及 3.9 的特性会标出版本。最后一节列出实战中最容易踩的坑。一、配置管理用字典承载一层默认配置配置最常见的需求是「默认值 用户覆盖」。字典的合并正好干这个。# 适用于 Python 3.8DEFAULTS {host: 127.0.0.1,port: 8000,debug: False,timeout: 30,}def load_config(user_config):# 3.8 及以前用 {**a, **b} 造新字典不改动默认值merged {**DEFAULTS, **user_config}return mergedcfg load_config({port: 9000, debug: True})print(cfg[port]) # 9000用户值覆盖默认print(DEFAULTS[port]) # 8000默认值没被改如果你在Python 3.9可以写得更直观merged DEFAULTS | user_config。注意{**a, **b}与a | b都返回新字典不修改原来的DEFAULTS这在配置场景里很重要——默认值一旦被污染后续调用全乱。访问配置项时优先用get给默认值避免缺键崩溃# 适用于 Python 3.8print(cfg.get(retries, 3)) # 3没配就用兜底二、计数统计出现次数统计词频、投票、日志里各状态出现次数都是同一个套路。# 适用于 Python 3.8words [apple, banana, apple, cherry, banana, apple]counts {}for w in words:counts[w] counts.get(w, 0) 1 # 缺键当 0 再加一print(counts) # {apple: 3, banana: 2, cherry: 1}同样的活标准库的collections.Counter一行就能干而且缺键自动返回0# 适用于 Python 3.8from collections import Countercounts Counter(words)print(counts.most_common(2)) # [(apple, 3), (banana, 2)]一般规则临时数一数用get也行要排序取前几名、要做计数之间的加减Counter更合适。三、分组用setdefault建桶把一列「(类别, 元素)」按类别归堆是字典最实用的用法之一。# 适用于 Python 3.8records [(水果, 苹果),(蔬菜, 胡萝卜),(水果, 香蕉),(蔬菜, 土豆),(水果, 苹果),]buckets {}for category, item in records:buckets.setdefault(category, []).append(item)print(buckets)# {水果: [苹果, 香蕉, 苹果], 蔬菜: [胡萝卜, 土豆]}这行的关键在setdefault(category, [])键不在就先把[]放进去并返回它键在直接返回已有的列表。列表是原地append所以改动会体现在字典里。如果连setdefault都不想写可以用collections.defaultdict(list)它会在缺键时自动调用list()建桶# 适用于 Python 3.8from collections import defaultdictbuckets defaultdict(list)for category, item in records:buckets[category].append(item)要注意defaultdict的副作用即使只是「读」一个不存在的键也会把它建出来。四、简易缓存记忆化同一个参数反复计算可以把结果存进字典复用。手写版本如下# 适用于 Python 3.8cache {}def slow_square(n):if n in cache:return cache[n]result n * n # 这里假装是很贵的计算cache[n] resultreturn resultprint(slow_square(4)) # 16print(slow_square(4)) # 16第二次直接命中缓存键必须可哈希所以参数是列表时不能直接做键得先转成元组# 适用于 Python 3.8def key_of(seq):return tuple(seq) # 列表 → 元组才能当键手写缓存只是演示原理。生产代码里更该用标准库的functools.lru_cache# 适用于 Python 3.8from functools import lru_cachelru_cache(maxsize128)def fib(n):return n if n 2 else fib(n - 1) fib(n - 2)print(fib(10)) # 55五、和 JSON 互转注意键只能是字符串字典和 JSON 对象长得几乎一样json模块能直接互转。# 适用于 Python 3.8import jsondata {name: 小明, age: 18, tags: [a, b]}text json.dumps(data, ensure_asciiFalse)print(text) # {name: 小明, age: 18, tags: [a, b]}back json.loads(text)print(back[name]) # 小明这里有个必须知道的区别Python 字典的键可以是元组、整数、浮点而 JSON 对象的键只能是字符串。序列化时非字符串键会被转换反序列化回来就变不回原类型了。# 适用于 Python 3.8import jsond {1: one, (2, 3): pair}print(json.dumps(d)) # {1: one, [2, 3]: pair}# 键被转成字符串1 和 [2, 3]所以「字典 → JSON → 字典」不保证能还原。如果键是元组或整数往返一趟就变了。常见坑点坑一合并配置时改坏了默认值。# 适用于 Python 3.8# ❌ 把用户配置直接 update 进默认值默认值被永久污染DEFAULTS {port: 8000}DEFAULTS.update({port: 9000}) # DEFAULTS 变成 {port: 9000}# ✅ 用原默认值造新字典不动原对象BASE {port: 8000}merged {**BASE, port: 9000}print(BASE) # {port: 8000}print(merged) # {port: 9000}坑二计数时忘了给默认值。# 适用于 Python 3.8# ❌ 直接累加会 KeyErrorcounts {}counts[apple] 1 # KeyError: apple# ✅ 用 get 或 defaultdictcounts[apple] counts.get(apple, 0) 1坑三用get建桶数据悄悄丢了。# 适用于 Python 3.8records [(水果, 苹果), (蔬菜, 胡萝卜)]# ❌ get 只在缺键时返回一个临时列表append 后没人保存数据丢了buckets {}for k, v in records:buckets.get(k, []).append(v) # 临时列表用完即弃print(buckets) # {}什么都没存进去# ✅ 用 setdefault它会把新桶写回字典for k, v in records:buckets.setdefault(k, []).append(v)print(buckets) # {水果: [苹果], 蔬菜: [胡萝卜]}坑四把列表塞进 JSON 的键。# 适用于 Python 3.8# ❌ 列表不能当字典键也就无法参与序列化d {[a]: 1} # TypeError构造时就失败# ✅ 键用字符串需要复合键先自己拼好d {a,b: 1}坑五以为 JSON 往返能还原键的类型。# 适用于 Python 3.8# ❌ 整数键往返后会变字符串import jsond {1: one}back json.loads(json.dumps(d))print(back) # {1: one}# ✅ 需要整数键就自己转回去back {int(k): v for k, v in back.items()}坑六自己手写的缓存没有上限。# 适用于 Python 3.8# ❌ 缓存只增不减调用参数一多就把内存吃满cache {}def f(n):if n not in cache:cache[n] n * nreturn cache[n]# ✅ 用 lru_cache 设个上限超出自动淘汰最久未用的from functools import lru_cachelru_cache(maxsize128)def g(n):return n * n坑七把defaultdict当只读字典。# 适用于 Python 3.8# ❌ 一次读取就写入了新键from collections import defaultdictg defaultdict(list)if g[missing]: # 读取时自动创建 missingpass# ✅ 只读用 in 或 getif missing in g:pass总结场景推荐做法注意配置合并{**defaults, **user}3.9 可用 \计数Counter或get(k, 0) 1缺键先给 0分组setdefault(k, [])/defaultdict(list)列表是原地追加缓存手写 dict 或lru_cache键必须可哈希JSONjson.dumps/json.loads键只能是字符串字典在实战里的价值不在于它有多少方法而在于它能把「对应关系」直接表达出来。用对它配置、计数、分组、缓存这四类常见需求都会变得短而清晰但一定要盯住两条红线键必须可哈希以及JSON 往返不保留非字符串键的类型。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑