前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >厌倦了 if else !升级到 Python-3.10 解锁新语法。

厌倦了 if else !升级到 Python-3.10 解锁新语法。

作者头像
初代庄主
发布2023-02-20 10:51:20
7061
发布2023-02-20 10:51:20
举报
文章被收录于专栏:初代庄主初代庄主

概要

年轻时听广告上讲:“人生就像一场旅行,不必在乎目的地,在乎的是沿途的风景以及看风景的心情。”

长大后学一门技术,感觉这过程也像是一场旅行,过程中不断收获知识,把时间拉长发现还收获了故事。下面就谈一谈 Python 从 if else 优化到 match case 做到性能优雅两全的故事吧!

tips: 不想看过程,想直接看性能测试结果的可以直接看文章的最后两节。


Python 是一门非常重 if else 的语言

以前 Python 真的是把 if else 用到了极致,比如说 Python 里面没有三元运算符( xx ? y : z ) 无所谓,它可以用 if else 整一个。

代码语言:javascript
复制
x = True if 100 > 0 else False

离谱的事还没有完,if else 这两老六还可以分别与其它语法结合,其中又数 else 玩的最野。

a: else 可以和 try 玩到一起,当 try 中没有引发异常的时候 else 块会得到执行。

代码语言:javascript
复制
#!/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 块能得到执行。

代码语言:javascript
复制
#!/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 那么多的副业;常见的就是列表推导。以过滤出列表中的偶数为例,传统上我们的代码可能是这样的。

代码语言:javascript
复制
#!/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()

使用列表推导可以一行解决。

代码语言:javascript
复制
#!/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 !!!

代码语言:javascript
复制
#!/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 。

代码语言:javascript
复制
#!/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 。测试代码会记录耗时和内存开销,耗时小的性能就好。详细的代码如下。

代码语言:javascript
复制
#!/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 好一点。大致语法像这样。

代码语言:javascript
复制
    match xxx:
        case aaa:
            ...
        case bbb:
            ... 
        case ccc:
            ...
        case ddd:
            ...

光说不练,假把式!改一下我们的测试代码然后比较一下三者的性能差异。

代码语言:javascript
复制
#!/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!

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

本文分享自 初代庄主 微信公众号,前往查看

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

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

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