字典推导式和字典合并是Python开发中处理键值对数据的两个核心技巧。字典推导式能在一行内完成过滤、键值转换和新字典的构建,而字典合并则广泛用于多来源配置覆盖、默认值合并和聚合数据场景。下面从基础语法到高级用法和实战配置系统,一次性梳理清楚。
一、字典推导式基础与条件过滤
字典推导式的基本语法为:- {key_expr: value_expr for item in iterable if condition}
复制代码 最简单的例子是从 range 构建平方映射:- squares = {x: x ** 2 for x in range(6)}
- print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
复制代码 通过 zip 将两个列表组合成字典:- keys = ['name', 'age', 'city']
- values = ['小明', 25, '北京']
- person = {k: v for k, v in zip(keys, values)}
- print(person) # {'name': '小明', 'age': 25, 'city': '北京'}
复制代码
带条件的推导式可以过滤数据。例如只保留值大于 2 的项:- numbers = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
- filtered = {k: v for k, v in numbers.items() if v > 2}
- print(filtered) # {'c': 3, 'd': 4, 'e': 5}
复制代码 也可以根据键前缀提取配置项,并做键名清理:- config = {
- 'DB_HOST': 'localhost',
- 'DB_PORT': 5432,
- 'CACHE_HOST': 'redis://localhost',
- 'CACHE_PORT': 6379,
- 'APP_NAME': 'MyApp',
- }
- db_config = {k: v for k, v in config.items() if k.startswith('DB_')}
- print(db_config) # {'DB_HOST': 'localhost', 'DB_PORT': 5432}
- cache_config = {
- k.replace('CACHE_', '').lower(): v
- for k, v in config.items()
- if k.startswith('CACHE_')
- }
- print(cache_config) # {'host': 'redis://localhost', 'port': 6379}
复制代码
二、键和值的变换及反转
对键或值进行统一变换:- original = {'a': 1, 'b': 2, 'c': 3}
- uppercase_keys = {k.upper(): v for k, v in original.items()}
- print(uppercase_keys) # {'A': 1, 'B': 2, 'C': 3}
- prices = {'apple': 10, 'banana': 5, 'orange': 8}
- with_tax = {k: v * 1.1 for k, v in prices.items()}
- print(with_tax) # {'apple': 11.0, 'banana': 5.5, 'orange': 8.8}
复制代码
反转字典(键值互换)时要注意值重复的情况:- original = {'a': 1, 'b': 2, 'c': 1}
- reversed_dict = {v: k for k, v in original.items()}
- print(reversed_dict) # {1: 'c', 2: 'b'} 'a'被'c'覆盖了
复制代码 如果需要保留所有键,可使用 setdefault 构建值到键列表的映射:- def safe_reverse(d):
- result = {}
- for k, v in d.items():
- result.setdefault(v, []).append(k)
- return result
- print(safe_reverse({'a': 1, 'b': 2, 'c': 1})) # {1: ['a', 'c'], 2: ['b']}
复制代码
三、嵌套推导式与条件表达式
从嵌套数据构建字典,例如从元组列表分组构建分类目录:- products = [
- ('电子产品', '手机', 2999),
- ('电子产品', '电脑', 5999),
- ('食品', '苹果', 5),
- ('食品', '香蕉', 3),
- ('服装', 'T恤', 99),
- ]
- # 使用 defaultdict 简化
- from collections import defaultdict
- catalog = defaultdict(dict)
- for category, item, price in products:
- catalog[category][item] = price
- print(dict(catalog)) # {'电子产品': {'手机': 2999, '电脑': 5999}, ...}
复制代码
在推导式中使用条件表达式对值进行分级:- scores = {'小明': 85, '小红': 92, '小刚': 78, '小李': 95, '小王': 60}
- graded = {
- k: ('优秀' if v >= 90 else '良好' if v >= 80 else '及格' if v >= 60 else '不及格')
- for k, v in scores.items()
- }
- print(graded)
- # {'小明': '良好', '小红': '优秀', '小刚': '良好', '小李': '优秀', '小王': '及格'}
复制代码
从列表推导式过渡到字典推导式,例如构建首字母索引:- words = ['apple', 'banana', 'cherry', 'date', 'elderberry']
- index = {ch: [w for w in words if w.startswith(ch)]
- for ch in set(w[0] for w in words)}
- print(index)
- # {'a': ['apple'], 'b': ['banana'], 'c': ['cherry'], 'd': ['date'], 'e': ['elderberry']}
复制代码
四、字典合并的四种方式
Python 提供了多种字典合并方式,需根据版本和需求选择。
1. | 运算符(Python 3.9+,最推荐,返回新字典)- defaults = {'host': 'localhost', 'port': 8080, 'debug': False}
- overrides = {'port': 9090, 'debug': True, 'timeout': 30}
- config = defaults | overrides
- print(config)
- # {'host': 'localhost', 'port': 9090, 'debug': True, 'timeout': 30}
复制代码
2. |= 原地合并(Python 3.9+)- config = {'host': 'localhost', 'port': 8080}
- config |= {'debug': True, 'port': 9090}
- print(config)
- # {'host': 'localhost', 'port': 9090, 'debug': True}
- # 可接受任何可迭代键值对
- config |= [('timeout', 30), ('retries', 3)]
- print(config)
- # {'host': 'localhost', 'port': 9090, 'debug': True, 'timeout': 30, 'retries': 3}
复制代码
3. **解包(Python 3.5+,返回新字典,但键必须是字符串)- d1 = {'a': 1, 'b': 2}
- d2 = {'c': 3, 'd': 4}
- d3 = {'a': 100, 'e': 5}
- merged = {**d1, **d2, **d3}
- print(merged) # {'a': 100, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
复制代码
4. update() 方法(Python 3.0+,原地修改,参数形式灵活)- d = {'a': 1, 'b': 2}
- d.update({'c': 3})
- d.update(d=4, e=5)
- d.update([('f', 6)])
- print(d) # {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
复制代码
方式对比简表:
| 方式 | 版本要求 | 新字典 | 原地修改 | 键限制 |
|------|----------|-------|----------|--------|
| `|` | 3.9+ | 是 | 否 | 任意可哈希 |
| `|=` | 3.9+ | 否 | 是 | 任意可哈希 |
| `**` | 3.5+ | 是 | 否 | 仅字符串 |
| update | 全部 | 否 | 是 | 多种形式 |
选择建议:
- Python 3.9+ 且需要新字典 → d1 | d2
- Python 3.9+ 且原地修改 → d1 |= d2
- Python 3.5~3.8 且需要新字典 → {**d1, **d2}
- 所有版本原地修改 → d1.update(d2)
五、实战:配置管理系统
下面实现一个支持层级合并的 Config 类,完整展示字典推导式与合并的应用。- class Config:
- """多层级配置管理系统"""
- def __init__(self, defaults=None):
- self._config = defaults.copy() if defaults else {}
- def load(self, **overrides):
- # 过滤掉 None 值
- valid_overrides = {k: v for k, v in overrides.items() if v is not None}
- self._config |= valid_overrides
- def load_from_file(self, file_config):
- # 只加载已知的配置项
- known_keys = self._config.keys()
- filtered = {k: v for k, v in file_config.items() if k in known_keys}
- self._config |= filtered
- def get(self, key, default=None):
- return self._config.get(key, default)
- def to_dict(self):
- return self._config.copy()
- def clone(self):
- return Config(self._config.copy())
- # 使用示例
- default_config = {
- 'host': 'localhost',
- 'port': 8080,
- 'debug': False,
- 'log_level': 'INFO',
- 'max_connections': 100,
- 'timeout': 30,
- }
- app_config = Config(default_config)
- app_config.load(port=9090, debug=True)
- file_config = {
- 'host': 'prod.server.com',
- 'port': 443,
- 'debug': False,
- 'log_level': 'WARNING',
- 'unknown_key': 'should_be_ignored',
- }
- app_config.load_from_file(file_config)
- print('最终配置:')
- for k, v in app_config.to_dict().items():
- print(f' {k}: {v}')
复制代码
六、字典合并的经典模式
1. 默认值 + 覆盖(参数合并)- def create_user(**kwargs):
- defaults = {'role': 'user', 'active': True, 'level': 1}
- return defaults | kwargs # kwargs 覆盖 defaults
- print(create_user(name='小明', role='admin'))
- # {'role': 'admin', 'active': True, 'level': 1, 'name': '小明'}
复制代码
2. 深度合并(嵌套字典递归)- def merge_deep(base, override):
- result = base.copy()
- for key, value in override.items():
- if key in result and isinstance(result[key], dict) and isinstance(value, dict):
- result[key] = merge_deep(result[key], value)
- else:
- result[key] = value
- return result
- base = {
- 'database': {'host': 'localhost', 'port': 5432, 'pool': {'min': 1}},
- 'cache': {'host': 'localhost', 'port': 6379},
- }
- override = {
- 'database': {'port': 5433, 'pool': {'max': 20}},
- }
- merged = merge_deep(base, override)
- print(merged)
- # {'database': {'host': 'localhost', 'port': 5433, 'pool': {'min': 1, 'max': 20}},
- # 'cache': {'host': 'localhost', 'port': 6379}}
复制代码
3. 多源数据聚合(后面覆盖前面)- def aggregate_data(*sources):
- result = {}
- for source in sources:
- if source:
- result |= source
- return result
- user_from_db = {'id': 1, 'name': '小明', 'email': 'xm@test.com'}
- user_from_api = {'name': '小明(更新)', 'phone': '13800138000'}
- user_from_cache = {'last_login': '2025-06-01'}
- print(aggregate_data(user_from_db, user_from_api, user_from_cache))
- # {'id': 1, 'name': '小明(更新)', 'email': 'xm@test.com', 'phone': '13800138000', 'last_login': '2025-06-01'}
复制代码
七、常见陷阱与注意事项
- | 运算符优先级较低,与条件表达式混用时务必加括号。例如 `d = ({'a': 1} | {'b': 2}) if condition else {}`。
- **解包不支持非字符串键,若字典键为整数则改用 | 或 update。
- 字典推导式在 Python 3+ 中有自己的局部作用域,不会污染外部同名变量。
- 合并操作前检查源是否为 None,避免 TypeError。可写成 `merged = (config or {}) | {}`。
掌握字典推导式与合并技巧后,处理字典数据的代码会更加简洁、高效且易读。下次可进一步学习 collections 模块中的 defaultdict 和 OrderedDict,它们在特定场景下比普通字典更强大。 |