Python基础指南之字典推导式与字典合并技巧

 更新时间:2026年06月14日 09:28:37   作者:星河耀银海  
字典推导式通过简洁的语法可以实现字典的创建、过滤和转换,本文将介绍Python中字典推导式和字典合并的相关技巧,感兴趣的小伙伴可以跟随小编一起学习一下

一、开篇:让字典操作更优雅

前面我们学完了字典的创建CRUD遍历。今天我们要攻克两个让字典操作效率大幅提升的高级技巧:字典推导式字典合并

字典推导式之于字典,就像列表推导式之于列表。它能让你用一行代码完成"过滤+转换+构建新字典"的完整操作。而字典合并则是处理多来源配置、覆盖默认值、聚合数据时不可或缺的能力。

这两个技巧在实际开发中使用频率极高。掌握了它们,你处理字典数据的代码量通常会减少一半以上——更重要的是,代码的意图表达会更清晰。

二、字典推导式基础

2.1 基本语法

# 语法:{key_expr: value_expr for item in iterable}

# 最简单的推导式
squares = {x: x ** 2 for x in range(6)}
print(squares)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 等价于:
squares = {}
for x in range(6):
    squares[x] = x ** 2

# 从两个列表构建字典
keys = ['name', 'age', 'city']
values = ['小明', 25, '北京']
person = {k: v for k, v in zip(keys, values)}
print(person)  # {'name': '小明', 'age': 25, 'city': '北京'}

2.2 带条件的字典推导式

# 过滤——只保留满足条件的项
numbers = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

# 只保留值大于2的
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}

2.3 键和值的变换

# 变换键
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 = {'user_name': 'xiaoming', 'user_email': 'xm@test.com'}
cleaned = {
    k.replace('user_', '').upper(): v.upper()
    for k, v in original.items()
}
print(cleaned)  # {'NAME': 'XIAOMING', 'EMAIL': 'XM@TEST.COM'}

# 反转字典(键值互换)
original = {'a': 1, 'b': 2, 'c': 3}
reversed_dict = {v: k for k, v in original.items()}
print(reversed_dict)  # {1: 'a', 2: 'b', 3: 'c'}

# ⚠️ 注意:如果值有重复,后面的会覆盖前面的
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'覆盖了)

# 处理值重复的反转
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']}

三、字典推导式的高级用法

3.1 嵌套推导式

# 从嵌套数据构建字典

# 例:从元组列表构建嵌套字典
students = [
    ('小明', {'math': 85, 'english': 90}),
    ('小红', {'math': 92, 'english': 88}),
    ('小刚', {'math': 78, 'english': 85}),
]

# 找出数学成绩大于80的学生
math_above_80 = {
    name: scores
    for name, scores in students
    if scores['math'] > 80
}
print(math_above_80)
# {'小明': {'math': 85, 'english': 90}, '小红': {'math': 92, 'english': 88}}


# 从查询结果构建嵌套字典
products = [
    ('电子产品', '手机', 2999),
    ('电子产品', '电脑', 5999),
    ('食品', '苹果', 5),
    ('食品', '香蕉', 3),
    ('服装', 'T恤', 99),
]

# 构建 {类别: {商品: 价格}}
catalog = {}
for category, item, price in products:
    catalog.setdefault(category, {})[item] = price
print(catalog)
# {'电子产品': {'手机': 2999, '电脑': 5999}, ...}

# 更简洁:用字典推导式分组
from collections import defaultdict
catalog2 = defaultdict(dict)
for category, item, price in products:
    catalog2[category][item] = price
print(dict(catalog2))

3.2 条件表达式在推导式中的应用

scores = {'小明': 85, '小红': 92, '小刚': 78, '小李': 95, '小王': 60}

# if-else在键或值的表达式中
# 给及格和不及格的人不同标注
graded = {
    k: ('优秀' if v >= 90 else '良好' if v >= 80 else '及格' if v >= 60 else '不及格')
    for k, v in scores.items()
}
print(graded)
# {'小明': '良好', '小红': '优秀', '小刚': '良好', '小李': '优秀', '小王': '及格'}

# 键和值都加条件变换
transformed = {
    k.upper() if v >= 90 else k.lower(): v * 2 if v < 80 else v
    for k, v in scores.items()
}
print(transformed)

# 只对满足条件的键值对做变换(if在for后面=过滤)
high_scores = {
    k: v for k, v in scores.items() if v >= 80
}
print(high_scores)  # {'小明': 85, '小红': 92, '小李': 95}

3.3 从列表推导式到字典推导式

# 场景:统计词频
text = 'hello world hello python world hello'

# 方式一:普通循环
word_count = {}
for word in text.split():
    word_count[word] = word_count.get(word, 0) + 1

# 方式二:使用Counter(最简单)
from collections import Counter
word_count = dict(Counter(text.split()))

# 方式三:字典推导式 + set
unique_words = set(text.split())
word_count = {word: text.split().count(word) for word in unique_words}
# 注意:这种方式效率低(每个词都要count一遍全文)

# 场景:构建索引
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']}

四、字典合并的四种方式

4.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}
# 有重复键时,右边的值覆盖左边的

# 链式合并
d1 = {'a': 1}
d2 = {'b': 2}
d3 = {'c': 3}
merged = d1 | d2 | d3
print(merged)  # {'a': 1, 'b': 2, 'c': 3}

# 注意:| 运算符要求两边都是dict
# {1: 'a'} | [(2, 'b')]  # TypeError

4.2 |= 原地合并(Python 3.9+)

config = {'host': 'localhost', 'port': 8080}

# |= 原地修改现有字典
config |= {'debug': True, 'port': 9090}
print(config)
# {'host': 'localhost', 'port': 9090, 'debug': True}

# 等价于 config.update({'debug': True, 'port': 9090})

# 可以接受任何可迭代的键值对
config |= [('timeout', 30), ('retries', 3)]
print(config)
# {'host': 'localhost', 'port': 9090, 'debug': True, 'timeout': 30, 'retries': 3}

4.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}

# 同样的覆盖规则:后面的覆盖前面的
# d3的'a':100覆盖了d1的'a':1

# 在合并时插入额外的键
merged = {**d1, 'extra': 'value', **d2}
print(merged)  # {'a': 1, 'b': 2, 'extra': 'value', 'c': 3, 'd': 4}

# ** 解包的一个限制:键必须是字符串
# d = {**{1: 'one'}, **{2: 'two'}}  # TypeError(Python限制)
# 这种情况下用 | 或 update()

4.4 update()(Python 3.0+)

d = {'a': 1, 'b': 2}

# update() 原地合并,接受多种参数形式
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}

# update() 可以和 | 对比
d1 = {'a': 1, 'b': 2}
d2 = {'b': 20, 'c': 3}

# 方式A:合并+创建新字典(|)
d3 = d1 | d2
# d1不变,d3是新的

# 方式B:合并+原地修改(update)
d1.update(d2)
# d1被修改了

# 选择:需要保留原字典用 |,不需要保留用 update

五、合并方式的对比与选择

方式对比表

方式Python版本创建新字典原地修改要求键是字符串
| 运算符3.9+否(任意可哈希类型)
|= 原地合并3.9+否(任意可哈希类型)
** 解包3.5+是(仅限字符串键)
update()全部多种参数形式

选择建议:

  • Python 3.9+、需要新字典 → d1 | d2
  • Python 3.9+、原地修改 → d1 |= d2
  • Python 3.5-3.8、需要新字典 → {**d1, **d2}
  • 任何Python版本、原地修改 → d1.update(d2)
  • 需要兼容旧代码 → update()始终可用

六、实战:配置管理系统

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}')

七、字典合并的经典模式

# 模式一:默认值 + 覆盖
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': '小明'}


# 模式二:层级配置合并
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}}


# 模式三:多源数据聚合
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'}

final_user = aggregate_data(user_from_db, user_from_api, user_from_cache)
print(final_user)

八、常见陷阱和注意事项

# 陷阱一:| 运算符的优先级
# d = {'a': 1} | {'b': 2} if condition else {}
# 上面的代码可能不是你期望的意思
# 建议加括号
# d = ({'a': 1} | {'b': 2}) if condition else {}

# 陷阱二:** 解包不能处理非字符串键
# d = {**{1: 'a'}, **{2: 'b'}}  # TypeError
# 用 | 或 update 代替
d = {1: 'a'} | {2: 'b'}
print(d)  # {1: 'a', 2: 'b'}

# 陷阱三:推导式中的变量作用域
# 字典推导式有自己的局部作用域(Python 3+)
# 不会污染外部命名空间
x = 'outer'
d = {x: x * 3 for x in 'abc'}
print(d)    # {'a': 'aaa', 'b': 'bbb', 'c': 'ccc'}
print(x)    # 'outer'(外部变量不受影响)

# 陷阱四:空字典的处理
empty = {}
# empty | {}  # 这是OK的
# 但注意:None | {} 会报TypeError
config = None
# merged = config | {}  # TypeError(如果config是None)
# 正确:先检查
merged = (config or {}) | {}

九、本篇小结

字典推导式和合并操作是Python字典的两大高级利器:

字典推导式:

  • 语法:{key_expr: value_expr for item in iterable if condition}
  • 适合:过滤、键值转换、反转、构建新字典
  • 比等价的for循环更短、更快、更易读

字典合并(四种方式):

  • d1 | d2(Python 3.9+)—— 创建新字典,最推荐
  • d1 |= d2(Python 3.9+)—— 原地修改
  • {**d1, **d2}(Python 3.5+)—— 创建新字典,仅限字符串键
  • d1.update(d2)(所有版本)—— 原地修改,最通用

字典推导式和字典合并是日常开发中的高频操作。熟练掌握它们,你处理字典数据的代码会更简洁、更Pythonic。下一篇我们学习collections模块中的defaultdict和OrderedDict——两个在特定场景下比普通字典更好用的字典变体。

以上就是Python基础指南之字典推导式与字典合并技巧的详细内容,更多关于Python字典推导式与合并的资料请关注脚本之家其它相关文章!

相关文章

  • python求最大值最小值方法总结

    python求最大值最小值方法总结

    在本篇内容里小编给大家分享了关于python求最大值最小值方法以及实例内容,有兴趣的朋友们学习下。
    2019-06-06
  • python 还原梯度下降算法实现一维线性回归

    python 还原梯度下降算法实现一维线性回归

    这篇文章主要介绍了python 还原梯度下降算法实现一维线性回归,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • Pycharm中配置使用Anaconda的虚拟环境进行项目开发的图文教程

    Pycharm中配置使用Anaconda的虚拟环境进行项目开发的图文教程

    今天在一台电脑上跑环境的时候,发现已经装了Pytorch了,但是运行没有用,提示报错:OSError: [WinError 126] 找不到指定的模块,但其实cmd进入虚拟环境是可以调用torch的,故本文给大家介绍了Pycharm中配置使用Anaconda的虚拟环境进行项目开发的图文教程
    2024-09-09
  • OpenCV-Python实现图像梯度与Sobel滤波器

    OpenCV-Python实现图像梯度与Sobel滤波器

    在实际应用中我们只需要将图像矩阵与Sobel滤波器卷积就可以得到图像的梯度矩阵了。具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • Python实现单例模式的五种写法总结

    Python实现单例模式的五种写法总结

    单例模式(Singleton Pattern) 是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。本文为大家整理了五种Python实现单例模式的写法,需要的可以参考一下
    2022-08-08
  • 关于python pycharm中输出的内容不全的解决办法

    关于python pycharm中输出的内容不全的解决办法

    这篇文章主要介绍了关于python pycharm中输出的内容不全的解决办法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-01-01
  • python爬虫之pyppeteer库简单使用

    python爬虫之pyppeteer库简单使用

    Puppeteer 是 Google 基于 Node.js 开发的一个工具,有了它我们可以通过 JavaScript 来控制 Chrome 浏览器的一些操作,当然也可以用作网络爬虫上,其 API 极其完善,功能非常强大
    2021-07-07
  • python析构函数用法及注意事项

    python析构函数用法及注意事项

    在本篇文章里小编给大家整理的是一篇关于python析构函数用法及注意事项,有需要的朋友们可以学习参考下。
    2021-06-06
  • Python的时间模块datetime详解

    Python的时间模块datetime详解

    本文给大家分享的是在python中的时间模块datetime的使用方法,注意事项以及简单示例,有需要的小伙伴可以参考下
    2017-04-04
  • PyTorch中torch.nn.Linear实例详解

    PyTorch中torch.nn.Linear实例详解

    torch.nn是包含了构筑神经网络结构基本元素的包,在这个包中可以找到任意的神经网络层,下面这篇文章主要给大家介绍了关于PyTorch中torch.nn.Linear的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06

最新评论