概要
年轻时听广告上讲:“人生就像一场旅行,不必在乎目的地,在乎的是沿途的风景以及看风景的心情。”
长大后学一门技术,感觉这过程也像是一场旅行,过程中不断收获知识,把时间拉长发现还收获了故事。下面就谈一谈 Python 从 if else 优化到 match case 做到性能与优雅两全的故事吧!
tips: 不想看过程,想直接看性能测试结果的可以直接看文章的最后两节。
Python 是一门非常重 if else 的语言
以前 Python 真的是把 if else 用到了极致,比如说 Python 里面没有三元运算符( xx ? y : z ) 无所谓,它可以用 if else 整一个。
x = True if 100 > 0 else False
离谱的事还没有完,if else 这两老六还可以分别与其它语法结合,其中又数 else 玩的最野。
a: else 可以和 try 玩到一起,当 try 中没有引发异常的时候 else 块会得到执行。
#!/usr/bin/env python3
# -*- coding: utf8 -*-
def main():
try:
# ...
pass
except Exception as err:
pass
else:
print("this is else block")
finally:
print("finally block")
if __name__ == "__main__":
main()
b: else 也可以配合循环语句使用,当循环体中没有执行 break 语句时 else 块能得到执行。
#!/usr/bin/env python3
# -*- coding: utf8 -*-
def main():
for i in range(3):
pass
else:
print("this is else block")
while False:
pass
else:
print("this is else block")
if __name__ == "__main__":
main()
c: if 相对来说就没有 else 那么多的副业;常见的就是列表推导。以过滤出列表中的偶数为例,传统上我们的代码可能是这样的。
#!/usr/bin/env python3
# -*- coding: utf8 -*-
def main():
result = []
numers = [1, 2, 3, 4, 5]
for number in numers:
if number % 2 == 0:
result.append(number)
print(result)
if __name__ == "__main__":
main()
使用列表推导可以一行解决。
#!/usr/bin/env python3
# -*- coding: utf8 -*-
def main():
numers = [1, 2, 3, 4, 5]
print( [_ for _ in numers if _ % 2 == 0] )
if __name__ == "__main__":
main()
看起来这些增强都还可以,但是对于类似于 switch 的这些场景,就不理想了。
没有 switch 语句 if else 顶上
对于 Python 这种把 if else 在语法上用到极致的语言,没有 switch 语句没关系的,它可以用 if else !!!
#!/usr/bin/env python3
# -*- coding: utf8 -*-
def fun(times):
"""这个函数不是我们测试的重点这里直接留白
Parameter
---------
times: int
"""
pass
def main(case_id: int):
"""由 case_id 到调用函数还有其它逻辑,这里为了简单统一处理在 100 * case_id
Parameter
---------
times: int
"""
if case_id == 1:
fun(100 * 1)
elif case_id == 2:
fun(100 * 2)
elif case_id == 3:
fun(100 * 3)
elif case_id == 4:
fun(100 * 4)
if __name__ == "__main__":
main(1)
这个代码写出来大家应该发现了,这样的代码像流水账一样一点都不优雅,用 Python 的话来说,这个叫一点都不 Pythonic !其它语言不好说,对于 Python 来讲不优雅就是有罪。
前面铺垫了这么多,终于快到重点了。社区提出了一个相对优雅的写法,新写法完全不用 if else 。
#!/usr/bin/env python3
# -*- coding: utf8 -*-
def fun(times):
pass
# 用字典以 case 为键,要执行的函数对象为值,这样做到按 case 路由
routers = {
1: fun,
2: fun,
3: fun,
4: fun
}
def main(case_id: int):
routers[case_id](100 * case_id)
if __name__ == "__main__":
main(1)
可以看到新的写法下,代码确实简洁了不少;从另一个角度来看社区也完成了一次进化,从之前抱着 if else 这个传家宝不放,到完全不用 if else 。也算是非常有意思吧。
新写法也不是没有问题;性能!性能!还是他妈的性能不行!
if else 和宝典写法性能测试
在说测试结果之前,先介绍一下我的开发环境,腾讯云的虚拟机器,Python 版本是 Python-3.12.0a3 。测试代码会记录耗时和内存开销,耗时小的性能就好。详细的代码如下。
#!/usr/bin/env python3
# -*- coding: utf8 -*-
import timeit
import tracemalloc
tracemalloc.start()
def fun(times):
"""这个函数不是我们测试的重点这里直接留白
Parameter
---------
times: int
"""
pass
# 定义 case 到 操作的路由字典
routers = {
1: fun,
2: fun,
3: fun,
4: fun
}
def main(case_id: int):
"""用于测试 if else 写法的耗时情况
Parametr
--------
case_id: int
不同 case 的唯一标识
Return
------
None
"""
if case_id == 1:
fun(100 * 1)
elif case_id == 2:
fun(100 * 2)
elif case_id == 3:
fun(100 * 3)
elif case_id == 4:
fun(100 * 4)
def main(case_id: int):
"""测试字典定法的耗时情况
Parametr
--------
case_id: int
不同 case 的唯一标识
Return
------
None
"""
routers[case_id](100 * case_id)
if __name__ == "__main__":
# 1. 记录开始时间、内存
# 2. 性能测试
# 3. 记录结束时间和总的耗时情况
start_current, start_peak = tracemalloc.get_traced_memory()
start_at = timeit.default_timer()
for i in range(10000000):
main((i % 4) + 1)
end_at = timeit.timeit()
cost = timeit.default_timer() - start_at
end_current, end_peak = tracemalloc.get_traced_memory()
print(f"time cost = {cost} .")
print(f"memery cost = {end_current - start_current}, {end_peak - start_peak}")
下面直接上我在开发环境的测试结果。
文字版本。
可以看到字典写法虽然优雅了一些,但是它在性能上是不行的。故事讲到这里,我们这次的主角要上场了。
match case 新语法
Python-3.10 版本引入了一个新的语法 match case ,这个新语法和其它语言的 switch case 差不多。在性能上比字典写法好一点,在代码的优雅程度上比 if else 好一点。大致语法像这样。
match xxx:
case aaa:
...
case bbb:
...
case ccc:
...
case ddd:
...
光说不练,假把式!改一下我们的测试代码然后比较一下三者的性能差异。
#!/usr/bin/env python3
# -*- coding: utf8 -*-
import timeit
import tracemalloc
tracemalloc.start()
def fun(times):
"""这个函数不是我们测试的重点这里直接留白
Parameter
---------
times: int
"""
pass
# 定义 case 到 操作的路由字典
routers = {
1: fun,
2: fun,
3: fun,
4: fun
}
def main(case_id: int):
"""用于测试 if else 写法的耗时情况
Parametr
--------
case_id: int
不同 case 的唯一标识
Return
------
None
"""
if case_id == 1:
fun(100 * 1)
elif case_id == 2:
fun(100 * 2)
elif case_id == 3:
fun(100 * 3)
elif case_id == 4:
fun(100 * 4)
def main(case_id: int):
"""测试字典定法的耗时情况
Parametr
--------
case_id: int
不同 case 的唯一标识
Return
------
None
"""
routers[case_id](100 * case_id)
def main(case_id: int):
"""测试 match case 写法的耗时情况
Parametr
--------
case_id: int
不同 case 的唯一标识
Return
------
None
"""
match case_id:
case 1:
fun(100 * 1)
case 2:
fun(100 * 2)
case 3:
fun(100 * 3)
case 4:
fun(100 * 4)
if __name__ == "__main__":
# 1. 记录开始时间、内存
# 2. 性能测试
# 3. 记录结束时间和总的耗时情况
start_current, start_peak = tracemalloc.get_traced_memory()
start_at = timeit.default_timer()
for i in range(10000000):
main((i % 4) + 1)
end_at = timeit.timeit()
cost = timeit.default_timer() - start_at
end_current, end_peak = tracemalloc.get_traced_memory()
print(f"time cost = {cost} .")
print(f"memery cost = {end_current - start_current}, {end_peak - start_peak}")
可以看到 match case 耗时还是比较理想的。
详细的数据如下。
总结
Python3 真是一直在变化,可能是因为我没有什么其它爱好吧!平时没事就看看官方文档;看久了感觉这鬼东西就是一本故事书。
都到这里了,是时候图穷匕见了!帮忙点个“分享” + “收藏” + “点赞” + “在看” 我想涨几个粉,让我更有动力的把故事写下去 thx!
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有