MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Python替代switch case的多种方式

2024-04-175.0k 阅读

1. 字典映射替代 switch case

在许多编程语言中,switch case 语句是一种用于根据不同的值执行不同代码块的控制结构。然而,Python 并没有原生的 switch case 语句。不过,我们可以使用字典映射来实现类似的功能。

1.1 简单字典映射示例

假设我们有一个根据数字返回对应星期几的需求。在其他语言中,可能会使用 switch case 来实现,而在 Python 中,我们可以这样做:

def get_day_name(day_number):
    days = {
        1: "Monday",
        2: "Tuesday",
        3: "Wednesday",
        4: "Thursday",
        5: "Friday",
        6: "Saturday",
        7: "Sunday"
    }
    return days.get(day_number, "Invalid day number")


day_num = 3
print(get_day_name(day_num))

在这个例子中,我们创建了一个字典 days,键是数字,值是对应的星期几的名称。通过调用字典的 get 方法,我们可以根据传入的数字获取对应的星期名称。如果传入的数字不在字典的键中,get 方法会返回我们指定的默认值 “Invalid day number”。

1.2 字典映射函数调用

字典映射不仅可以返回简单的值,还可以用于调用不同的函数。假设我们有一些根据不同操作执行不同计算的需求。

def add(a, b):
    return a + b


def subtract(a, b):
    return a - b


def multiply(a, b):
    return a * b


def divide(a, b):
    if b == 0:
        return "Cannot divide by zero"
    return a / b


def perform_operation(operator, a, b):
    operations = {
        '+': add,
        '-': subtract,
        '*': multiply,
        '/': divide
    }
    operation_func = operations.get(operator)
    if operation_func:
        return operation_func(a, b)
    return "Invalid operator"


op = '*'
num1 = 5
num2 = 3
print(perform_operation(op, num1, num2))

这里我们定义了四个基本运算函数 addsubtractmultiplydivide。然后在 perform_operation 函数中,我们创建了一个字典 operations,键是操作符,值是对应的函数。根据传入的操作符,我们从字典中获取相应的函数并调用它,传入两个操作数。

1.3 字典映射的优势与局限

  • 优势
    • 简洁明了:代码结构清晰,易于理解和维护。例如在上述根据数字获取星期名称的例子中,字典的键值对一目了然。
    • 灵活性高:可以轻松地添加、删除或修改映射关系。如在操作符映射函数的例子中,如果需要添加新的运算,只需要在字典中增加一个键值对即可。
  • 局限
    • 仅适用于简单映射:对于复杂的条件判断,如包含多个条件组合的情况,字典映射会变得难以管理。例如,如果我们需要根据多个条件来决定执行的操作,单纯的字典映射就不太适用了。
    • 类型一致性要求:字典的键需要是可哈希的,并且通常在使用中期望键的类型保持一致。如果需要使用不同类型的键进行映射,可能需要额外的处理。

2. if - elif - else 链替代 switch case

虽然 if - elif - else 链不是专门为替代 switch case 设计的,但它可以实现类似的功能,并且在 Python 中是一种非常常用的条件判断结构。

2.1 简单 if - elif - else 示例

还是以根据数字返回星期几为例,使用 if - elif - else 可以这样写:

def get_day_name(day_number):
    if day_number == 1:
        return "Monday"
    elif day_number == 2:
        return "Tuesday"
    elif day_number == 3:
        return "Wednesday"
    elif day_number == 4:
        return "Thursday"
    elif day_number == 5:
        return "Friday"
    elif day_number == 6:
        return "Saturday"
    elif day_number == 7:
        return "Sunday"
    else:
        return "Invalid day number"


day_num = 4
print(get_day_name(day_num))

这里通过一系列的 if - elif 条件判断,根据 day_number 的值返回相应的星期名称。如果所有条件都不满足,则执行 else 分支,返回 “Invalid day number”。

2.2 复杂条件判断

if - elif - else 链在处理复杂条件判断时更具优势。假设我们要根据学生的成绩给出不同的评价,并且成绩的评定标准有多种条件。

def get_grade_evaluation(score):
    if score >= 90:
        if score == 100:
            return "Perfect score! Outstanding!"
        return "Excellent"
    elif score >= 80:
        return "Very Good"
    elif score >= 70:
        return "Good"
    elif score >= 60:
        return "Pass"
    else:
        return "Fail"


student_score = 85
print(get_grade_evaluation(student_score))

在这个例子中,我们不仅根据成绩范围给出评价,还对满分的情况进行了特殊处理。这种复杂的条件嵌套使用 if - elif - else 链可以很方便地实现。

2.3 if - elif - else 链的优势与局限

  • 优势
    • 强大的表达能力:能够处理各种复杂的条件逻辑,无论是简单的单一条件判断,还是多层嵌套的复杂条件组合。
    • 广泛适用性:在 Python 编程中随处可见,所有 Python 开发者都熟悉这种结构,代码的可读性对于团队协作很友好。
  • 局限
    • 代码冗长:当条件较多时,if - elif - else 链会变得很长,降低代码的可读性。例如在根据数字获取星期名称的例子中,相比于字典映射,if - elif - else 的代码行数更多。
    • 效率问题:在条件较多时,Python 需要依次检查每个 ifelif 条件,这可能会导致性能下降,尤其是在条件判断复杂的情况下。

3. match - case 语句(Python 3.10+)

从 Python 3.10 开始,引入了 match - case 语句,它在功能上类似于其他语言中的 switch case 语句,为 Python 提供了一种更优雅的模式匹配方式。

3.1 基本 match - case 示例

以获取星期几为例,使用 match - case 可以这样写:

def get_day_name(day_number):
    match day_number:
        case 1:
            return "Monday"
        case 2:
            return "Tuesday"
        case 3:
            return "Wednesday"
        case 4:
            return "Thursday"
        case 5:
            return "Friday"
        case 6:
            return "Saturday"
        case 7:
            return "Sunday"
        case _:
            return "Invalid day number"


day_num = 6
print(get_day_name(day_num))

match 关键字后面跟着要匹配的值,这里是 day_number。每个 case 子句定义了一个匹配模式和相应的代码块。如果 day_number 的值与某个 case 子句中的模式匹配,就会执行该 case 子句中的代码。case _ 是一个通配符,表示匹配其他所有未明确指定的情况。

3.2 模式匹配的高级用法

match - case 不仅支持简单的值匹配,还支持更复杂的模式匹配。例如,我们可以匹配列表、元组或对象的结构。

def process_data(data):
    match data:
        case [1, x]:
            return f"First element is 1, second element is {x}"
        case (2, y, z):
            return f"First element is 2, second is {y}, third is {z}"
        case {"name": name, "age": age} if age > 18:
            return f"{name} is an adult"
        case _:
            return "Unrecognized data pattern"


data1 = [1, 5]
data2 = (2, "John", 25)
data3 = {"name": "Alice", "age": 15}
print(process_data(data1))
print(process_data(data2))
print(process_data(data3))

在这个例子中,第一个 case 匹配一个列表,其第一个元素是 1,并将第二个元素赋值给 x。第二个 case 匹配一个元组,其第一个元素是 2,并将第二个和第三个元素分别赋值给 yz。第三个 case 匹配一个字典,该字典包含 nameage 键,并且 age 大于 18。

3.3 match - case 语句的优势与局限

  • 优势
    • 简洁与清晰:对于简单的多条件判断,match - case 语句的结构比 if - elif - else 链更简洁,可读性更高。例如在获取星期几的例子中,代码结构更加紧凑。
    • 强大的模式匹配:支持复杂的模式匹配,如对列表、元组和字典的结构匹配,这在处理结构化数据时非常有用。
  • 局限
    • 版本限制:只能在 Python 3.10 及以上版本使用,如果项目需要兼容旧版本 Python,就无法使用这种方式。
    • 学习成本:对于习惯了传统 if - elif - else 或之前没有接触过类似模式匹配结构的开发者,需要一定时间学习和适应这种新的语法。

4. 函数调度器替代 switch case

函数调度器是一种通过函数来管理不同功能模块调用的方式,它也可以用来模拟 switch case 的行为。

4.1 创建函数调度器

我们可以创建一个函数,该函数根据传入的参数来决定调用哪个子函数。

def func1():
    return "Function 1 executed"


def func2():
    return "Function 2 executed"


def func3():
    return "Function 3 executed"


def function_dispatcher(func_key):
    functions = {
        'func1': func1,
        'func2': func2,
        'func3': func3
    }
    return functions.get(func_key, lambda: "Invalid function key")()


result1 = function_dispatcher('func2')
print(result1)

在这个例子中,function_dispatcher 函数接受一个 func_key 参数。它内部创建了一个字典 functions,将函数名映射到实际的函数对象。通过 get 方法获取对应的函数,如果键不存在,则返回一个默认的 lambda 函数,该函数返回 “Invalid function key”。然后调用获取到的函数并返回结果。

4.2 带参数的函数调度

函数调度器也可以处理带参数的函数。

def add(a, b):
    return a + b


def multiply(a, b):
    return a * b


def function_dispatcher_with_args(func_key, *args, **kwargs):
    functions = {
        'add': add,
      'multiply': multiply
    }
    func = functions.get(func_key)
    if func:
        return func(*args, **kwargs)
    return "Invalid function key"


result2 = function_dispatcher_with_args('multiply', 3, 4)
print(result2)

这里 function_dispatcher_with_args 函数接受 func_key 以及可变位置参数 *args 和可变关键字参数 **kwargs。根据 func_key 获取相应的函数,并将参数传递给该函数进行调用。

4.3 函数调度器的优势与局限

  • 优势
    • 可扩展性:很容易添加新的函数和功能。只需要在字典中添加新的键值对,将新函数名映射到函数对象即可。
    • 模块化:将不同的功能封装在各个函数中,函数调度器负责管理调用,使代码结构更具模块化,易于维护和理解。
  • 局限
    • 命名管理:如果函数名较多,需要注意命名的规范性和避免冲突。例如在函数调度器中,如果两个不同功能的函数取了相同的名字,就会导致错误。
    • 不适合复杂条件:对于复杂的条件判断,单纯的函数调度器可能无法很好地处理,还是需要结合其他条件判断结构来实现完整的逻辑。

5. 类方法映射替代 switch case

通过类的方法映射,我们也可以实现类似于 switch case 的功能,这种方式在面向对象编程中较为常用。

5.1 简单类方法映射示例

class Switcher:
    def case1(self):
        return "Case 1 executed"

    def case2(self):
        return "Case 2 executed"

    def case3(self):
        return "Case 3 executed"

    def default(self):
        return "Invalid case"

    def switch(self, case_key):
        method_name = 'case' + str(case_key)
        method = getattr(self, method_name, self.default)
        return method()


switcher = Switcher()
result3 = switcher.switch(2)
print(result3)

在这个例子中,我们定义了一个 Switcher 类,其中包含多个以 case 开头命名的方法。switch 方法根据传入的 case_key 构建方法名,然后使用 getattr 函数获取对应的方法。如果方法不存在,则使用 default 方法。最后调用获取到的方法并返回结果。

5.2 类方法映射处理复杂逻辑

类方法映射不仅可以处理简单的返回值情况,还可以处理复杂的业务逻辑。

class Calculator:
    def add(self, a, b):
        return a + b

    def subtract(self, a, b):
        return a - b

    def multiply(self, a, b):
        return a * b

    def divide(self, a, b):
        if b == 0:
            return "Cannot divide by zero"
        return a / b

    def calculate(self, operator, a, b):
        method_name = operator
        method = getattr(self, method_name, lambda a, b: "Invalid operator")
        return method(a, b)


calculator = Calculator()
result4 = calculator.calculate('multiply', 5, 3)
print(result4)

这里 Calculator 类包含多个数学运算方法。calculate 方法根据传入的操作符 operator 获取相应的方法,并传入操作数 ab 进行计算。如果操作符对应的方法不存在,则返回 “Invalid operator”。

5.3 类方法映射的优势与局限

  • 优势
    • 面向对象特性:符合面向对象编程的原则,将相关的功能封装在类中,提高了代码的可维护性和可扩展性。例如在 Calculator 类中,如果需要添加新的运算方法,只需要在类中定义新的方法即可。
    • 代码组织清晰:通过类的结构,将不同功能的方法进行归类,使得代码结构更加清晰,易于理解和管理。
  • 局限
    • 类的开销:相比于简单的函数或字典映射,类的定义和实例化会带来一定的开销。如果只是为了简单的条件映射,使用类方法映射可能会显得过于复杂。
    • 命名空间问题:在类中定义多个方法时,需要注意方法名的唯一性,避免命名冲突。如果类中的方法名与其他模块或类中的方法名相同,可能会导致意外的错误。

6. 比较各种替代方式

6.1 性能比较

  • 字典映射:在简单的查找场景下,字典的查找时间复杂度为 O(1),性能较好。例如在根据数字获取星期几的字典映射示例中,无论字典中有多少个键值对,查找时间基本保持不变。但如果字典很大,且需要进行复杂的条件判断(如多个条件组合),性能会受到影响,因为需要额外的逻辑来处理复杂条件。
  • if - elif - else:当条件较少时,性能影响不大。但随着条件数量的增加,Python 需要依次检查每个条件,时间复杂度变为 O(n),性能会逐渐下降。特别是在条件判断复杂的情况下,每次检查都可能涉及复杂的计算,这会进一步降低性能。
  • match - case 语句:在 Python 3.10 及以上版本中,match - case 语句在性能上有一定的优化。它采用了类似于字典的查找方式(对于简单值匹配),在某些情况下性能优于 if - elif - else 链。但对于复杂的模式匹配,性能会受到模式复杂度的影响。例如,匹配复杂的列表或字典结构可能需要更多的计算资源。
  • 函数调度器:函数调度器本身的性能主要取决于字典查找的性能,因为它内部也是通过字典来映射函数。所以在简单情况下,性能与字典映射类似。但如果函数调用本身有较大开销(如函数内部执行复杂的计算),那么整体性能会受到函数执行时间的影响。
  • 类方法映射:类方法映射涉及到类的实例化和方法查找。类的实例化会有一定的开销,而方法查找通过 getattr 函数,在简单情况下性能尚可。但如果类中有大量的方法,方法查找的时间可能会增加。并且,如果类方法内部执行复杂逻辑,也会影响整体性能。

6.2 代码可读性比较

  • 字典映射:对于简单的映射关系,代码非常简洁明了。例如在根据数字获取星期几的字典映射示例中,键值对清晰地展示了数字与星期名称的对应关系。但当映射关系变得复杂,如需要处理多个条件组合时,可能需要添加额外的逻辑,这会降低代码的可读性。
  • if - elif - else:当条件较少时,代码可读性较好,因为条件判断的逻辑很直观。但随着条件数量的增加,代码会变得冗长,可读性会逐渐降低。特别是在多层嵌套的情况下,代码的缩进层次会增加,使得代码结构变得复杂,难以快速理解整个逻辑。
  • match - case 语句:在 Python 3.10 及以上版本中,match - case 语句对于简单和复杂的模式匹配都提供了一种相对简洁和清晰的表达方式。例如在处理列表、元组或字典的模式匹配时,代码结构能够清晰地展示匹配的模式和相应的操作。相比于 if - elif - else 链,它在多条件判断时的代码结构更加紧凑,可读性更好。
  • 函数调度器:函数调度器通过将不同功能封装在函数中,然后通过字典映射来调用,代码结构具有一定的模块化特点,可读性较好。特别是当函数的功能明确且命名规范时,很容易理解整个调度过程。但如果函数名较多且不规范,可能会影响代码的可读性。
  • 类方法映射:类方法映射将相关功能封装在类中,通过类的结构来组织代码。对于熟悉面向对象编程的开发者来说,这种方式的代码可读性较好。类的方法名通常能够清晰地表达其功能,并且通过 switch 方法来统一调度,使得代码逻辑较为清晰。但对于不熟悉面向对象编程的人来说,可能需要花费一些时间来理解类的结构和方法调用的逻辑。

6.3 适用场景比较

  • 字典映射:适用于简单的一对一映射场景,如根据固定值返回固定结果的情况。例如在根据数字获取星期几、根据操作符执行简单运算等场景中,字典映射是一种很好的选择。但对于复杂的条件判断和多条件组合的情况,不太适用。
  • if - elif - else:适用于各种条件判断场景,尤其是复杂的条件逻辑,如多层嵌套的条件判断、包含多个条件组合的情况。它是 Python 中最通用的条件判断结构,在各种项目中都广泛应用。但在简单的多条件判断场景下,相比于 match - case 或字典映射,代码可能会显得冗长。
  • match - case 语句:适用于 Python 3.10 及以上版本的项目,特别是需要进行模式匹配的场景。无论是简单的值匹配还是复杂的列表、元组、字典结构匹配,match - case 语句都能提供简洁清晰的实现方式。对于新开发的项目且不需要兼容旧版本 Python 的情况,match - case 语句是一个很好的选择。
  • 函数调度器:适用于将不同功能模块化为函数,并且需要根据某个参数来动态调用这些函数的场景。例如在一个插件系统中,根据用户选择的插件名称来调用相应的插件函数,函数调度器就很适用。它强调功能的模块化和动态调用。
  • 类方法映射:适用于面向对象编程的场景,特别是当相关功能可以自然地封装在一个类中时。例如在一个计算器类中,通过类方法映射来根据操作符执行不同的计算方法。它利用了面向对象的特性,提高了代码的可维护性和可扩展性。

通过对以上多种替代 switch case 的方式进行详细分析,开发者可以根据项目的具体需求、Python 版本以及个人编程习惯来选择最合适的方式,以实现高效、清晰和可维护的代码。