Python中如何使用类似switch-case的逻辑实现

我们知道,python是没有switch语句的,所以当我们要实现这样结构的逻辑时:

var index = 10

switch index {
   case 100  :
      print( "index 的值为 100")
   case 10,15  :
      print( "index 的值为 10 或 15")
   case 5  :
      print( "index 的值为 5")
   default :
      print( "默认 case")
}

经常需要用多个if-else来实现。除此之外,我们还可以考虑用字典对应提取的方式来实现,下面我们给出四种实现switch的方法,并对比这四种方法的运行时间

something = 'something'

# 第一种,多次使用if-else结构
if something == 'this':
    the_thing = 1
elif something == 'that':
    the_thing = 2
elif something == 'there':
    the_thing = 3
else:
    the_thing = 4

# 第二种,用get设置默认值的字典提取
options = {'this': 1, 'that': 2, 'there': 3}
the_thing = options.get(something, 4)

# 第三种,用if-else配合不设置默认值的字典提取
options = {'this': 1, 'that': 2, 'there': 3}
if something in options:
    the_thing = options[something]
else:
    the_thing = 4

# 第四种,用collections模块设置默认值进行字典提取
from collections import defaultdict
default_options = defaultdict(lambda: 4, {'this': 1, 'that': 2, 'there': 3})
the_thing = default_options[something]

下面我们对比一下这几种方式提取的速度,分成两种情况

  • 判断的内容在字典中
  • 判断的内容不在字典中
  • 在ifelse.py文件中输入如下内容

    import time
    from collections import defaultdict
    
    # 计算运行时间的装饰器
    def run_time(func):
        def wrapper(*args, **kw):
            start = time.time()
            func(*args, **kw)
            end = time.time()
            print('running', end-start, 's')
        return wrapper
    
    # 准备好两个字典
    options = {'this': 1, 'that': 2, 'there': 3}
    default_options = defaultdict(lambda: 4, {'this': 1, 'that': 2, 'there': 3})
    
    
    # 四种方法都定义成函数
    # 接受参数something即待判断值
    # 每次循环10000000次
    @run_time
    def first(something):
        for i in range(10000000):
            if something == 'this':
                the_thing = 1
            elif something == 'that':
                the_thing = 2
            elif something == 'there':
                the_thing = 3
            else:
                the_thing = 4
    
    @run_time
    def second(something):
        for i in range(10000000):
            the_thing = options.get(something, 4)
    
    
    @run_time
    def third(something):
        for i in range(10000000):
            if something in options:
                the_thing = options[something]
            else:
                the_thing = 4
    
    @run_time
    def forth(something):
        for i in range(10000000):
            the_thing = default_options[something]
    
    # 调用函数
    if __name__ == '__main__':
        # 判断的内容不在字典中
        first('something')
        second('something')
        third('something')
        forth('something')
        print('-'*20)
        # 判断的内容在字典中
        first('this')
        second('this')
        third('this')
        forth('this')

    在命令行多次运行

    python ifelse.py

    得到结果如下

    -------------第一次---------------
    running 1.8487958908081055 s
    running 1.63755202293396 s
    running 0.7807505130767822 s
    running 0.6786513328552246 s
    --------------------
    running 0.7807483673095703 s
    running 2.075996160507202 s
    running 1.0349910259246826 s
    running 0.740731954574585 s
    
    -------------第二次---------------
    
    running 1.7757258415222168 s
    running 1.6395549774169922 s
    running 0.8408102989196777 s
    running 0.7977871894836426 s
    --------------------
    running 0.710662841796875 s
    running 1.9098539352416992 s
    running 1.042982578277588 s
    running 0.8197875022888184 s
    
    -------------第三次---------------
    
    running 1.5885050296783447 s
    running 1.8237719535827637 s
    running 0.9819226264953613 s
    running 0.78375244140625 s
    --------------------
    running 0.6226155757904053 s
    running 1.634549617767334 s
    running 0.947911262512207 s
    running 0.6586313247680664 s

    从结果中可以看出
    1.四种方法之间的对比,后两种方法明显比前两种方法快,且最后一种方法总是最快的。
    2.待判断内容是否在字典中设置的对比

  • 第一种全程if-else判断的情况下,早判断出来程序就会早结束,所以if-else判断的内容顺序是有讲究的
  • 而从字典里提取则没有看出显著的不同
  • 由于使用collections模块中的defaultdict虽然最快,但是会占用较多内存,所以最推荐的是第三种方法,使用if-else配合无默认字典提取方法。

    在Python中,没有内置的switch语句,但是可以使用多种方式实现类似switch的操作。以下是几种实现方法:

    1. 使用字典:
    
    
    pythondef switch(key):
    return {
    'a': 'case a',
    'b': 'case b',
    'c': 'case c',
    }.get(key, 'default')
    
    print(switch('a')) # 输出: case a
    print(switch('d')) # 输出: default
    1. 使用 if-elif-else 语句:
    
    
    pythondef switch(key):
    if key == 'a':
    return 'case a'
    elif key == 'b':
    return 'case b'
    elif key == 'c':
    return 'case c'
    else:
    return 'default'
    
    print(switch('a')) # 输出: case a
    print(switch('d')) # 输出: default
    1. 使用函数装饰器和字典:
    
    
    pythondef switch(key):
    @dict_of_functions.get(key, lambda: 'default')
    def default():
    return 'case a'
    def case_b():
    return 'case b'
    def case_c():
    return 'case c'
    
    dict_of_functions = {
    'a': default,
    'b': case_b,
    'c': case_c,
    }
    return switch(key)()
    
    print(switch('a')) # 输出: case a
    print(switch('d')) # 输出: default
    1. 使用类装饰器和字典:
    
    
    pythonclass Switch:
    def __init__(self, key):
    self.key = key
    
    @property
    def case(self):
    return {
    'a': 'case a',
    'b': 'case b',
    'c': 'case c',
    }.get(self.key, 'default')
    
    print(Switch('a').case) # 输出: case a
    print(Switch('d').case) # 输出: default

    以上就是Python中实现类似switch操作的几种方法。

    python语言中,没有内置switch函数,如果用if-else语句的话,当分支数量很大时,会显得很臃肿,下面是使用python中的字典,实现switch语句功能的方法。

    # 设置flag的值,用于选择执行哪个函数
    flag = 0
    # 设置自定义函数
    def get_function_1():
        # 函数功能
        return 'Function_1'
    
    def get_function_2():
        # 函数功能
        return 'Function_2'
    
    def get_function_3():
        # 函数功能
        return 'Function_3'
    
    def get_default():
        # 函数功能
        return 'Others'
    
    # 字典中不同的值对应不同的自定义函数
    switcher = {
        0: get_function_1,
        1: get_function_2,
        2: get_function_3
    }
    # 根据flag的值决定执行哪一个函数,如果输入的值在字典中没有,则执行get_default函数
    output = switcher.get(flag, get_default)()
    print("The output of switcher is: ", output)

    如果语句不复杂,也可以使用lambda表达式,可以使代码更简洁,lambda表达式介绍见

    Python lambda介绍 – Goodpy – 博客园​www.cnblogs.com/evening/archive/2012/03/29/2423554.html

    下面是利用了字典和lambda表达式,实现switch语句功能

    flag = 0
    # 如果字典中没有flag的值,则执行get_default函数
    def get_default(x):
        # 函数功能
        return 'None'
    
    switcher = {
        0: lambda x:x+1,
        1: lambda x:x**2,
        2: lambda x:abs(x)
    }
    
    output = switcher.get(flag, get_default)(5)
    print("The output of switcher is: ", output)

    作者:书海阅读岛
    链接:https://www.zhihu.com/question/591024612/answer/2953371599
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
     

    Python已有特性

    1. Python中的函数是可以作为变量存在的,也就是可以将函数复制给变量,变量可以作为字典的value存在,也就是函数可以作为字典的value,对应了switch中的基本特性1
    2. if-else可以让程序存在多种不同的分支,可以用来实现基本特性2中的默认逻辑
    3. 字典的key和value是一对一或者多对一的,就是每个key对应一个value,但是一个value可以有多个key;这点对应了switch的基本特性3

    需要添加的特性

    从Python已有特性上来说,我们看起来不需要添加什么新的新特性,只需要把已有特性组合下就行

    组合特性

    这里以一个课本中的题目来做例子:百分制转字母分,也就是——90分及以上是A,80分及以上是B,70分及以上是C,60分及以上是D,60分以下都是E

    1. 定义一个字典,key是条件,value是函数,把条件和对应的逻辑组合起来
    # 定义各种函数
    def is_a():
        return 'A'
    
    
    def is_b():
        return 'B'
    
    
    def is_c():
        return 'C'
    
    
    def is_d():
        return 'D'
    
    
    def is_e():
        return 'E'
    
    # 为了方便处理,分数先除以10向下取整,按取整结果选执行逻辑
    # 定义switch-case跳转表,这里90分及以上是A,80分及以上是B,70分及以上是C,60分及以上是D
    switch_dict = {
        10: is_a,
        9: is_a,
        8: is_b,
        7: is_c,
        6: is_d,
    }
    1. 用if-else语句处理能找的到条件的执行逻辑和找不到条件的执行逻辑,也就是实现匹配和默认情况
    if score_div_ten in switch_dict.keys():
        # 在switch_dict里有对应的逻辑
        return switch_dict[score_div_ten]()
    else:
        # 没在switch_dict里的按默认逻辑处理
        return is_e()

    全部代码

    #!/usr/bin/python
    # -*- coding: utf8 -*-
    import math
    
    
    def is_a():
        return 'A'
    
    
    def is_b():
        return 'B'
    
    
    def is_c():
        return 'C'
    
    
    def is_d():
        return 'D'
    
    
    def is_e():
        return 'E'
    
    
    def score_to_af(score):
        # 分数除以10以后向下取整,算是属于几十分
        score_div_ten = math.floor(score / 10)
    
        # 定义switch-case跳转表,这里90分及以上是A,80分及以上是B,70分及以上是C,60分及以上是D,60分以下都是E(default)
        switch_dict = {
            10: is_a,
            9: is_a,
            8: is_b,
            7: is_c,
            6: is_d,
        }
    
        if score_div_ten in switch_dict.keys():
            return switch_dict[score_div_ten]()
        else:
            return is_e()
    
    
    if __name__ == '__main__':
        for score in range(0, 101):
            print('score:%d, af:%s' % (score, score_to_af(score)))

    执行结果

    score:0, af:E
    score:1, af:E
    score:2, af:E
    score:3, af:E
    score:4, af:E
    score:5, af:E
    score:6, af:E
    score:7, af:E
    score:8, af:E
    score:9, af:E
    score:10, af:E
    score:11, af:E
    score:12, af:E
    score:13, af:E
    score:14, af:E
    score:15, af:E
    score:16, af:E
    score:17, af:E
    score:18, af:E
    score:19, af:E
    score:20, af:E
    score:21, af:E
    score:22, af:E
    score:23, af:E
    score:24, af:E
    score:25, af:E
    score:26, af:E
    score:27, af:E
    score:28, af:E
    score:29, af:E
    score:30, af:E
    score:31, af:E
    score:32, af:E
    score:33, af:E
    score:34, af:E
    score:35, af:E
    score:36, af:E
    score:37, af:E
    score:38, af:E
    score:39, af:E
    score:40, af:E
    score:41, af:E
    score:42, af:E
    score:43, af:E
    score:44, af:E
    score:45, af:E
    score:46, af:E
    score:47, af:E
    score:48, af:E
    score:49, af:E
    score:50, af:E
    score:51, af:E
    score:52, af:E
    score:53, af:E
    score:54, af:E
    score:55, af:E
    score:56, af:E
    score:57, af:E
    score:58, af:E
    score:59, af:E
    score:60, af:D
    score:61, af:D
    score:62, af:D
    score:63, af:D
    score:64, af:D
    score:65, af:D
    score:66, af:D
    score:67, af:D
    score:68, af:D
    score:69, af:D
    score:70, af:C
    score:71, af:C
    score:72, af:C
    score:73, af:C
    score:74, af:C
    score:75, af:C
    score:76, af:C
    score:77, af:C
    score:78, af:C
    score:79, af:C
    score:80, af:B
    score:81, af:B
    score:82, af:B
    score:83, af:B
    score:84, af:B
    score:85, af:B
    score:86, af:B
    score:87, af:B
    score:88, af:B
    score:89, af:B
    score:90, af:A
    score:91, af:A
    score:92, af:A
    score:93, af:A
    score:94, af:A
    score:95, af:A
    score:96, af:A
    score:97, af:A
    score:98, af:A
    score:99, af:A
    score:100, af:A

    思路2的思考过程

    一段if-elif-else代码

    def score_to_af2(score):
        # 分数除以10以后向下取整,算是属于几十分
        score_div_ten = math.floor(score / 10)
    
        if 9 <= score_div_ten <= 10:
            result = 'A'
        elif score_div_ten == 8:
            result = 'B'
        elif score_div_ten == 7:
            result = 'C'
        elif score_div_ten == 6:
            result = 'D'
        else:
            result = 'E'
        return result

    条件和执行逻辑

    很简单的代码,条件和一行简单的逻辑的对应关系,还是继续抽函数,抽出来每个条件的函数:

    # 定义各种函数
    def is_a():
        return 'A'
    
    
    def is_b():
        return 'B'
    
    
    def is_c():
        return 'C'
    
    
    def is_d():
        return 'D'
    
    
    def is_e():
        return 'E'

    然后代码变成:

    def score_to_af2(score):
        # 分数除以10以后向下取整,算是属于几十分
        score_div_ten = math.floor(score / 10)
    
        if 9 <= score_div_ten <= 10:
            result = is_a()
        elif score_div_ten == 8:
            result = is_b()
        elif score_div_ten == 7:
            result = is_c()
        elif score_div_ten == 6:
            result = is_d()
        else:
            result = is_e()
        return result

    条件和执行逻辑&Key和Value

    执行逻辑抽取完成,开始抽条件;因为条件和函数对应的关系就是字典中key和value的关系,所以可以抽成一个key是条件,value是函数的字典

    # 定义switch-case跳转表,这里90分及以上是A,80分及以上是B,70分及以上是C,60分及以上是D,60分以下都是E(default)
    switch_dict = {
        10: is_a,
        9: is_a,
        8: is_b,
        7: is_c,
        6: is_d,
    }

    抽完字典开始围绕字典写逻辑,和上面score_to_af的代码一致,以及最后成品代码也是一致,不再赘述。

     

    作者:Yuki-^_^

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python中如何使用类似switch-case的逻辑实现

    发表回复