合并 Python 中的字典
如何在 Python 中合并字典?
这取决于你对“合并”一词的具体定义。
在 Python 中使用 |
操作符合并字典
首先,让我们讨论合并字典的最简单方法,这通常已经足够满足你的需求。
以下是两个字典:
>>> context = {"language": "en", "timezone": "UTC"}
>>> more_context = {"title": "Home", "breadcrumbs": ["Home"]}
我们希望创建一个新的第三个字典,该字典将这两个字典的内容结合起来。这个新字典应包含两个初始字典中的所有键值对。
最简单的方法是使用管道操作符(|
):
>>> new_context = context | more_context
这会创建一个新的字典,其中包括了两个初始字典的所有项目:
>>> new_context
{'language': 'en', 'timezone': 'UTC', 'title': 'Home', 'breadcrumbs': ['Home']}
使用 |
操作符,本质上相当于创建一个新的空字典,然后通过遍历第一个字典和第二个字典中的所有项目,将它们添加到新字典中:
>>> new_context = {}
>>> for key, value in context.items():
... new_context[key] = value
...
>>> for key, value in more_context.items():
... new_context[key] = value
...
>>> new_context
{'language': 'en', 'timezone': 'UTC', 'title': 'Home', 'breadcrumbs': ['Home']}
使用 update
方法合并字典
如果我们希望直接在原字典上进行更新,该怎么办?
假设我们仍然使用前面的两个字典:
>>> context = {"language": "en", "timezone": "UTC"}
>>> more_context = {"title": "Home", "breadcrumbs": ["Home"]}
现在,希望将第一个字典(context
)更新为同时包含第二个字典(more_context
)中的所有项目。
我们可以通过遍历第二个字典,将每个键值对添加到第一个字典中:
>>> for key, value in more_context.items():
... context[key] = value
...
>>> context
{'language': 'en', 'timezone': 'UTC', 'title': 'Home', 'breadcrumbs': ['Home']}
但是,字典的 update
方法可以替代我们完成这些工作:
>>> context.update(more_context)
update
方法接收一个字典,并修改调用该方法的字典,使其包含两个字典中的所有键值对。
另外,还可以使用管道操作符的增强赋值语句:
>>> context |= more_context
这与字典的 update
方法功能相同,因此具体选择哪个方法取决于个人喜好。
使用 **
合并字典
我们还可以使用 Python 的双星号(**
)语法来合并字典:
>>> combined = {**context, **more_context}
这会创建一个新字典,与管道操作符(|
)的效果相同。
不过,我认为 {**a, **b}
的可读性稍差于 a | b
,所以在合并字典时,我通常优先选择管道操作符。
|
和 **
的区别
需要注意的是,管道操作符(|
)与双星号(**
)并不完全一样。
有时,a | b
和 {**a, **b}
的结果会有所不同。
例如,如果你使用管道操作符与一个自定义的映射类型(例如 defaultdict
)进行合并,返回的新字典会保留该自定义类型。
以下是两个 defaultdict
对象:
>>> from collections import defaultdict
>>> group1 = defaultdict(list)
>>> group1["giraffe"].append("Gerard")
>>> group2 = defaultdict(list)
>>> group2["ferret"].append("Francis")
>>> group1
defaultdict(<class 'list'>, {'giraffe': ['Gerard']})
>>> group2
defaultdict(<class 'list'>, {'ferret': ['Francis']})
如果使用管道操作符将这两个 defaultdict
对象合并,返回的将是一个 defaultdict
对象:
>>> group1 | group2
defaultdict(<class 'list'>, {'giraffe': ['Gerard'], 'ferret': ['Francis']})
而如果使用 **
语法合并这两个字典,返回的将是一个普通的 dict
对象:
>>> {**group1, **group2}
{'giraffe': ['Gerard'], 'ferret': ['Francis']}
使用管道操作符(|
)合并字典时,第一个字典的类型会决定最终合并结果的类型。而使用 **
合并字典时,结果始终是一个 dict
对象。
因此,在某些情况下,如果需要接受任意字典类型,但需要返回一个内置的 dict
类型,**
语法会更合适。不过,大多数情况下,|
的行为更符合实际需求。
合并时如何处理重复键
如果合并的两个字典中存在重复的键,会发生什么?
例如,以下两个字典中都包含 title
键:
>>> context = {"language": "en", "timezone": "UTC", "title": "Welcome"}
>>> more_context = {"title": "Home", "breadcrumbs": ["Home"]}
与通过 for
循环进行合并一样,在合并的过程中,最后一个值会覆盖重复键对应的值。
因为 more_context
位于 context
之后,title
的值将会是 Home
(而非 Welcome
):
>>> context | more_context
{'language': 'en', 'timezone': 'UTC', 'title': 'Home', 'breadcrumbs': ['Home']}
如果我们希望采用其他行为,比如在处理重复键时选择较大的值,该如何操作?以下示例中,我们希望当键重复时,保留更大的价格:
>>> prices1 = {"premium": 29.99, "basic": 9.99, "pro": 49.99}
>>> prices2 = {"basic": 7.99, "pro": 39.99}
遗憾的是,目前没有直接的快捷方式完成此需求。
我们可以通过复制第一个字典,然后遍历第二个字典,检查每个键是否在新字典中,并使用 get
方法比较并更新值:
>>> merged = prices1.copy()
>>> for plan, price in prices2.items():
... if price > merged.get(plan, 0):
... merged[plan] = price
...
>>> merged
{'premium': 29.99, 'basic': 9.99, 'pro': 49.99}
管道操作符 |
执行字典的“并集”操作
通常,合并两个字典最简单的方法是使用管道操作符:
>>> context = {"language": "en", "timezone": "UTC"}
>>> more_context = {"title": "Home", "breadcrumbs": ["Home"]}
>>> context | more_context
{'language': 'en', 'timezone': 'UTC', 'title': 'Home', 'breadcrumbs': ['Home']}
那么,为什么 Python 使用管道操作符(|
)来实现此操作,而不是加号操作符(+
)呢?
>>> context + more_context
Traceback (most recent call last):
File "<python-input-4>", line 1, in <module>
context + more_context
~~~~~~~~^~~~~~~~~~~~~~
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
加号操作符用于连接序列,但序列连接与字典合并有所不同。
字典需要处理重复键的情况,而序列则不需要。
管道操作符之所以被选择,是因为它已经被用于集合(set
)的合并操作:
>>> names = {"language", "timezone", "title"}
>>> more_names = {"title", "breadcrumbs"}
>>> names | more_names
{'language', 'timezone', 'breadcrumbs', 'title'}
管道操作符可以对集合进行“并集”操作。
你可以将字典的合并类比为两个字典的并集操作。