Python 基础:字典推导式完全入门
学习笔记:用
{k: v for ...}一行生成字典——语法、等价写法、过滤条件,以及在本项目里的真实用法。
前言
在 Python 里,除了用 for 循环一个个 dict[key] = value 填字典,还可以用 字典推导式(dict comprehension) 更简洁地生成 dict。
你在项目 src/config/configuration.py 里已经见过:
values: dict[str, Any] = {
f.name: os.environ.get(f.name.upper(), configurable.get(f.name))
for f in fields(cls)
if f.init
}
return cls(**{k: v for k, v in values.items() if v})
本文从基础语法讲起,再讲到这两行代码在干什么。
1. 什么是字典推导式?
基本形式:
{键表达式: 值表达式 for 变量 in 可迭代对象}
带过滤条件:
{键表达式: 值表达式 for 变量 in 可迭代对象 if 条件}
类比:
| 推导式 | 生成结果 |
|---|---|
列表推导式 [x for x in ...] |
list |
字典推导式 {k: v for ...} |
dict |
集合推导式 {x for ...} |
set |
2. 最简单的例子
2.1 从列表生成 dict
nums = [1, 2, 3, 4, 5]
squares = {n: n * n for n in nums}
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
读法:对每个 n,键是 n,值是 n * n。
2.2 等价于普通 for 循环
squares = {}
for n in nums:
squares[n] = n * n
字典推导式就是把这种「循环 + 赋值」压缩成一行。
3. 带 if 过滤
只保留满足条件的项:
nums = [1, 2, 3, 4, 5, 6]
evens = {n: n * n for n in nums if n % 2 == 0}
# {2: 4, 4: 16, 6: 36}
等价于:
evens = {}
for n in nums:
if n % 2 == 0:
evens[n] = n * n
注意: 这里的 if 写在 for 后面,表示「过滤」——不满足条件的根本不会进 dict。
4. 键和值可以是任意表达式
4.1 键、值都可以变换
words = ["apple", "banana", "cat"]
length_map = {word: len(word) for word in words}
# {'apple': 5, 'banana': 6, 'cat': 3}
upper_key = {word.upper(): word for word in words}
# {'APPLE': 'apple', 'BANANA': 'banana', 'CAT': 'cat'}
4.2 从两个列表「拉链」成 dict(常见技巧)
keys = ["name", "age", "city"]
values = ["Alice", 20, "Beijing"]
person = {k: v for k, v in zip(keys, values)}
# {'name': 'Alice', 'age': 20, 'city': 'Beijing'}
Python 3 里更常见的写法是直接 dict(zip(keys, values)),但推导式同样清晰。
5. 遍历 dict:.items() + 推导式
对已存在的 dict 做「变换」或「过滤」,常用 .items():
values = {
"max_step_num": 5,
"agent_type": None,
"mcp_settings": {"servers": {}},
}
filtered = {k: v for k, v in values.items() if v}
# {'max_step_num': 5, 'mcp_settings': {'servers': {}}}
拆解 .items()
values.items()
# dict_items([
# ('max_step_num', 5),
# ('agent_type', None),
# ('mcp_settings', {'servers': {}}),
# ])
每次循环得到一对 (k, v):
for k, v in values.items():
print(k, v)
# max_step_num 5
# agent_type None
# mcp_settings {'servers': {}}
if v 是什么意思?
在 Python 里,if v 判断 v 是否为「真值」(truthy):
| v | if v |
是否保留 |
|---|---|---|
5 |
真 | ✅ |
"hello" |
真 | ✅ |
{"a": 1} |
真 | ✅ |
None |
假 | ❌ |
"" |
假 | ❌ |
0 |
假 | ❌ |
[] |
假 | ❌ |
False |
假 | ❌ |
所以 {k: v for k, v in values.items() if v} 的含义是:
只保留值不为空/不为 None/不为 0 的键值对,生成一个新 dict。
等价写法:
filtered = {}
for k, v in values.items():
if v:
filtered[k] = v
6. 和 ** 一起用:解包成关键字参数
过滤后的 dict 常配合 ** 传给函数或类:
filtered = {"max_step_num": 5}
cls(**filtered)
# 等价于 cls(max_step_num=5)
完整链条:
return cls(**{k: v for k, v in values.items() if v})
{k: v for k, v in values.items() if v}→ 得到过滤后的 dict**{...}→ 展开成cls(字段名=值, ...)- 没出现在 dict 里的字段 → 使用 dataclass 默认值
7. 项目里的真实例子
例子 1:从 dataclass 字段生成配置 dict
values: dict[str, Any] = {
f.name: os.environ.get(f.name.upper(), configurable.get(f.name))
for f in fields(cls)
if f.init
}
| 部分 | 含义 |
|---|---|
for f in fields(cls) |
遍历 Configuration 每个 dataclass 字段 |
if f.init |
只处理会参与构造的字段 |
f.name |
字段名,如 "max_step_num" |
f.name.upper() |
环境变量名,如 "MAX_STEP_NUM" |
os.environ.get(..., configurable.get(f.name)) |
优先环境变量,否则用请求 config |
假设 Configuration 有字段 max_step_num、agent_type,configurable = {"max_step_num": 5}:
values = {
"max_step_num": 5,
"agent_type": None,
# ... 其他字段
}
例子 2:过滤无效值后创建对象
return cls(**{k: v for k, v in values.items() if v})
agent_type: None 被过滤掉 → Configuration 使用默认 AgentType.DEFAULT。
8. 和其他写法对比
8.1 dict comprehension vs 普通循环
# 推导式(简洁,适合简单逻辑)
{x: x * 2 for x in range(5)}
# 普通循环(复杂逻辑时更清晰)
result = {}
for x in range(5):
if some_complex_check(x):
result[x] = x * 2
建议: 逻辑简单用推导式;嵌套太多、分支太多就改回 for 循环,可读性更重要。
8.2 过滤:if v vs if v is not None
# if v:None、""、0、[] 都会过滤掉
{k: v for k, v in d.items() if v}
# if v is not None:只过滤 None,保留 0 和 ""
{k: v for k, v in d.items() if v is not None}
项目里用 if v 是因为:空值/None 都表示「没配置,用默认值」。
8.3 与 dict() + 生成器
dict((k, v) for k, v in values.items() if v) # 也可以,但不如 {} 直观
9. 常见误区
误区 1:键重复,后面的覆盖前面的
{x // 2: x for x in [1, 2, 3, 4]}
# {0: 4, 1: 3} ← 不是 4 个键,因为键冲突了
误区 2:把 if 位置写错
# 过滤(for 后面的 if)
{k: v for k, v in d.items() if v > 0}
# 条件表达式(键或值里的 if else)
{k: (v if v else 0) for k, v in d.items()}
两种 if 含义不同:
| 位置 | 作用 |
|---|---|
for ... if 条件 |
过滤,不满足的不进 dict |
值 if 条件 else 其他 |
三元表达式,决定值是什么 |
误区 3:推导式里副作用过多
# 不推荐:在推导式里 print / 写文件
{ k: print(k) or v for k, v in d.items() }
推导式适合纯转换,有副作用用普通 for 循环。
10. 小练习
练习 1: 列表转 dict,值为长度的 2 倍
words = ["ai", "agent", "mcp"]
# 目标: {'ai': 4, 'agent': 10, 'mcp': 6}
result = {w: len(w) * 2 for w in words}
练习 2: 只保留值大于 10 的项
scores = {"math": 90, "english": 5, "python": 88}
# 目标: {'math': 90, 'python': 88}
passed = {k: v for k, v in scores.items() if v > 10}
练习 3: 模拟项目里的过滤 + 解包
values = {"max_step_num": 5, "agent_type": None, "locale": "zh-CN"}
def fake_config(**kwargs):
print(kwargs)
fake_config(**{k: v for k, v in values.items() if v})
# 输出: {'max_step_num': 5, 'locale': 'zh-CN'}
11. 速查表
# 基本
{k: v for k, v in pairs}
# 从列表
{str(x): x for x in range(3)}
# 带过滤
{k: v for k, v in d.items() if 条件}
# 变换键或值
{k.upper(): v * 2 for k, v in d.items()}
# 配合 ** 传参
func(**{k: v for k, v in d.items() if v})
12. 小结
| 概念 | 记住 |
|---|---|
| 基本语法 | {键: 值 for x in 可迭代对象} |
| 过滤 | {k: v for k, v in d.items() if 条件} |
.items() |
同时拿到键和值 |
if v |
去掉 None、空串、0 等 falsy 值 |
**{...} |
把 dict 展开成关键字参数 |
字典推导式 = 用一行 for 循环生成 dict。读懂它,就能看懂项目里 Configuration.from_runnable_config 的配置合并逻辑。
记录日期:2026-05-28
更多推荐



所有评论(0)