前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >这样合并Python字典,可以让程序的运行效率提高4倍

这样合并Python字典,可以让程序的运行效率提高4倍

作者头像
蒙娜丽宁
发布2021-02-19 10:57:19
4.9K0
发布2021-02-19 10:57:19
举报
文章被收录于专栏:极客起源

为了让更多的人看到本文,请各位同学动动小手,点击右上角【...】,将本文分享到朋友圈,thanks!

摘要:在Python中,合并字典有多种方式,通过内建函数、运算符、自定义函数等,都可以完成合并字典的功能,但这些方式,哪些效率低,哪些效率高呢?本文将对这些合并字典的方式进行逐个深度详解,最后会比较这些方式,看看到底谁是效率之王!

现在提出一个问题:如何用一行代码合并两个Python字典,并返回合并结果。可能很多同学最先想到的是下面的代码:

代码语言:javascript
复制
x = {'a': 1, 'b': 2}
y = {'b': 10, 'c': 11}
x.update(y)
# {'a': 1, 'b': 10, 'c': 11}
print(x)

这段代码没有任何问题,通过update方法可以将x和y合并,但问题是update方法修改了x的值,将合并的结果赋给了x,而不是新生成一个变量。这样并没有通过一行代码合并两个字典,而且还修改了变量x的值。

当然,我们可以做一些改变,例如,先定义一个空的字典z,然后分别将x和y与z合并,代码如下:

代码语言:javascript
复制
x = {'a': 1, 'b': 2}
y = {'b': 10, 'c': 11}
z = {}
z.update(x)
z.update(y)
# {'a': 1, 'b': 10, 'c': 11}
print(x)

这段代码完美地将x和y合并,而且并未改变x和y的值,不过代码量比较多,仍然未使用一行代码合并两个字典。

下面就看看那些用一行代码解决合并字典的方法们:

1. Python 3.9的解决方案

如果读者使用Python 3.9,那简直太幸运了,因为Python 3.9可以直接通过“|”运算符合并两个字典,简直干净利索,代码如下:

代码语言:javascript
复制
z = x | y
print(z)

不过遗憾的是,“|”运算符只能合并字典,不能合并列表。

2. Python 3.5及以上版本的解决方案

如果读者使用的不是Python 3.9,但却是Python3.5或以上版本,如Python3.7、Python3.8等,可以采用双星(**)运算符合并两个字典,代码如下:

代码语言:javascript
复制
x = {'a': 1, 'b': 2}
y = {'b': 10, 'c': 11}
z = {**x, **y}
print(z)

这里的“**”表示将字典拆成key-value对形式传入,那么{**x, **y}就表示先将x和y拆成独立的key-value对,然后再将这些key-value对覆盖新字典。

除了“**”外,还有可以处理列表的“*”,例如,下面的代码可以合并两个列表。合并原理与“**”类似。

代码语言:javascript
复制
xx = [1,2,3]
yy = [4,5,6]
# 合并列表
zz = {*xx,*yy}
print(zz)

3. Python 3.4或一下版本的解决方案

如果读者使用的是Python3.4或更低的Python版本,如Python2.7,那么如果想用一行代码解决问题,就要自己编写函数了。基本的解决思路是先将x整个复制一份,变成z,然后再使用update函数合并z与y。实现代码如下:

代码语言:javascript
复制
def merge_two_dicts(x, y):
    z = x.copy()   # 复制x到z
    z.update(y)    # 将z与y合并,z已经改变
    return z

现在就可以使用下面的一行代码合并x和y了,而且x和y都没有改变。

代码语言:javascript
复制
print(merge_two_dicts(x,y))

如果还想合并不定数量的字典,如3个字典、5个字典,可以使用下面的函数:

代码语言:javascript
复制
def merge_dicts(*dict_args):   
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result

new_dict = {'x':20,'y':30}
# {'a': 1, 'b': 10, 'c': 11, 'x': 20, 'y': 30}
print(merge_dicts(x,y,new))

4. 深度合并

前面给出的案例只能浅层合并,如果想深度合并,可以使用递归的方式。例如,要合并下面两个字典:

代码语言:javascript
复制
xx = {'a':{1:{}}, 'b': {2:{}}}
yy = {'b':{10:{}}, 'c': {11:{}}}

这两个字典的每一个key,也是一个字典,现在需要合并每一个key表示的字典,合并的结果希望是如下形式:

代码语言:javascript
复制
{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}

要完成这个功能,需要使用deepcopy函数,代码如下:

代码语言:javascript
复制
from copy import deepcopy

def dict_of_dicts_merge(x, y):
    z = {}
    # 如果两个要合并的字典结构不一致,无法合并,返回None
    if not hasattr(x,'keys') or not hasattr(y,'keys'):
        return
    # 交集:b
    overlapping_keys = x.keys() & y.keys()

    for key in overlapping_keys:
        z[key] = dict_of_dicts_merge(x[key], y[key])
    for key in x.keys() - overlapping_keys:
        z[key] = deepcopy(x[key])
    for key in y.keys() - overlapping_keys:
        z[key] = deepcopy(y[key])
    return z

现在可以使用下面的代码合并xx和yy:

代码语言:javascript
复制
xx = {'a':{1:{}}, 'b': {2:{}}}
yy = {'b':{10:{}}, 'c': {11:{}}}
print(dict_of_dicts_merge(xx, yy))

如果要合并的两个字典的key对应的value,有一个不是字典,那么无法合并,在这种情况下,value就为None,例如,要合并下面两个字典:

代码语言:javascript
复制
xx = {'a':{1:{}}, 'b': {2:{}}}
yy = {'b':20, 'c': {11:{}}}
print(dict_of_dicts_merge(xx, yy))

由于yy的b是20,而xx的b是一个字典,所以b无法合并,因此执行这段代码,会输出如下结果:

代码语言:javascript
复制
{'b': None, 'a': {1: {}}, 'c': {11: {}}}

5. 其他合并字典的方式

除了前面介绍的几种合并字典的方式,还可以用下面的2种合并方式:

(1)for in 表达式

在Python中有一种语法,可以利用for in表达式生成列表或字典,因此,可以利用这个功能,将要合并的两个字典中的key和value单独提取出来,然后逐个写入新的字典,实现代码如下:

代码语言:javascript
复制
x = {'a': 1, 'b': 2}
y = {'b': 10, 'c': 11}
z = {k: v for d in (x, y) for k, v in d.items()}
# {'a': 1, 'b': 10, 'c': 11}
print(z)

要理解for in表达式是如何工作的,可以先提取下面的代码:

代码语言:javascript
复制
for d in(x,y) :
    print(d)

这段代码其实是将x和y作为元组的元素,输出的结果如下:

代码语言:javascript
复制
{'a': 1, 'b': 2}
{'b': 10, 'c': 11}

然后for k, v in d.items()就是对这个元组进行迭代,而k和v就是提取元组中每一个元素的key和value,然后再将k和v作为新字典的key和value插入。

(2)使用chain对象

通过chain对象,可以将多个字典转换为像链表一样的结果(这个链表是可迭代的),然后再使用dict对象将其转换为字典,实现代码如下:

代码语言:javascript
复制
from itertools import chain
x = {'a': 1, 'b': 2}
y = {'b': 10, 'c': 11}
print(dict(chain(x.items(), y.items())))

6. 性能大比拼

到现在为止,已经讲了很多种合并字典的方式,那么这些方式哪些效率高,哪些效率低呢?下面就来做一个实验。

代码语言:javascript
复制
from timeit import repeat
from itertools import chain

x = dict.fromkeys('abcdefg')
y = dict.fromkeys('efghijk')

print('x | y'.ljust(50,' '), min(repeat(lambda: x | y)))
print('{**x, **y}'.ljust(50,' '),min(repeat(lambda: {**x, **y})))
print('merge_two_dicts(x, y)'.ljust(50,' '), min(repeat(lambda: merge_two_dicts(x, y))))
print('{k: v for d in (x, y) for k, v in d.items()}'.ljust(50,' '), min(repeat(lambda: {k: v for d in (x, y) for k, v in d.items()})))
print('dict(chain(x.items(), y.items()))'.ljust(50,' '), min(repeat(lambda: dict(chain(x.items(), y.items())))))
print('dict(item for d in (x, y) for item in d.items())'.ljust(50,' '), min(repeat(lambda: dict(item for d in (x, y) for item in d.items()))))

其中repeat函数可以用来方便地测试一小段代码的执行时间,在默认情况下,repeat函数会将由lambda参数指定的表达式执行时间放大100万倍,也就是执行100万次这个表达式,然后统计时间总和,并且这一过程进行5遍,也就是说,repeat函数会执行lambda参数指定的表达式500万次,最后得到5组时间值(单位是秒),然后用min函数挑出最小的值。

执行这段代码,会输出如图1所示的结果:

图1

很明显,x | y的效率最高,两个字典合并100万次,只需要不到0.5秒,而最后一种方式最慢,需要2秒,所以最快的合并字典的方式比最慢的方式整整快了4倍。

- EOF -

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 极客起源 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档