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

枫铃3年前 (2021-09-30)Python196

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

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

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合并,代码如下:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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可以直接通过“|”运算符合并两个字典,简直干净利索,代码如下:

z = x | y
print(z)

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

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

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

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对覆盖新字典。

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

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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。实现代码如下:

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

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

print(merge_two_dicts(x,y))

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

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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. 深度合并
前面给出的案例只能浅层合并,如果想深度合并,可以使用递归的方式。例如,要合并下面两个字典:

xx = {'a':{1:{}}, 'b': {2:{}}}
yy = {'b':{10:{}}, 'c': {11:{}}}

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

{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}

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

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:

xx = {'a':{1:{}}, 'b': {2:{}}}
yy = {'b':{10:{}}, 'c': {11:{}}}
print(dict_of_dicts_merge(xx, yy))

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

xx = {'a':{1:{}}, 'b': {2:{}}}
yy = {'b':20, 'c': {11:{}}}
print(dict_of_dicts_merge(xx, yy))

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

{'b': None, 'a': {1: {}}, 'c': {11: {}}}

5. 其他合并字典的方式

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

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

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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表达式是如何工作的,可以先提取下面的代码:

for d in(x,y) :
    print(d)

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

{'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对象将其转换为字典,实现代码如下:

from itertools import chain
x = {'a': 1, 'b': 2}
y = {'b': 10, 'c': 11}
print(dict(chain(x.items(), y.items())))

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

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所示的结果:
在这里插入图片描述

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

相关文章

利用python同步windows和linux文件

写python脚本的初衷,每次在windows编辑完文件后,想同步到linux上去,只能够登录服务器,...

爬虫基本原理

爬虫基本原理 一、爬虫是什么? 百度百科和维基百科对网络爬虫的定义:简单来说爬虫就是抓取目标网站内容的工具,一般是根据定义的行...

Django 函数和方法的区别

函数和方法的区别 1、函数要手动传self,方法不用传 2、如果是一个函数,用类名去调用,如果是一个方法...

Django 知识补漏单例模式

单例模式:(说白了就是)创建一个类的实例。在 Python 中,我们可以用多种方法来实现单例模式&#x...

Django基础知识MTV

Django简介 Django是使用Python编写的一个开源Web框架。可以用它来快速搭建一个高性能的网站。 Django也是一个MVC框架。但是在Dj...

Python mysql 索引原理与慢查询优化

一 介绍 为何要有索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。