条件控制

Python 中的条件控制只有if一种,其语句格式为:

if 条件1:
	语句块1
elif 条件2:
	语句块2
else:
	语句块3

一个if语句中,可以有多个elif条件语句块,但是仅有一个else语句块。if语句可以嵌套在其他语句块结构中,但是要注意语句块的缩进。

这里需要注意以下两个使用要点:

  1. 每个条件后面都要使用冒号:,表示接下来是满足条件后要执行的语句块。冒号也是定义新语句块的符号。
  2. 一个语句块中的语句要使用相同的缩进。

Info

用于定义新语句块的冒号:,以后会在各个需要定义新语句块的语句末尾出现,需要牢记并熟悉这个语言特性。

在 Python 中,if语句的使用方法十分灵活。由于 Python 没有其他语言中的三元操作符,即?:操作,但是 Python 利用if语句实现了更加强大的选择赋值操作。这种操作与前面提到过的列表推导式十分相似,以下用一个示例来说明。

c = []
b = max(c) if len(c) > 0 else -1

在上例中,if被放在了一个表达式后面,这表示这个表达式的执行将由if条件来决定,如果条件不满足,则返回else语句的值。所以上例中b的值即为-1。如果列表c中有内容,不是空列表,则b的内容则是列表中的最大值。

关于 switch

从前面的基本控制语句中可以看出,Python 中没有提供 switch/case 语句。这是因为 Python 认为 switch/case 语法结构需要对参与判断的内容进行 Hash 和唯一性识别,与 Python 的优雅背道而驰。但实际上,我们依旧有多种方法来实现 switch/case 语法结构的替代。

最常用的替代方法是使用字典。

def case(value):
    return {
        'value1': some_value,
        'value2': lambda x: x + 1
    }.get(value, None)

case(some_value)

使用字典来实现 switch/case 结构依靠字典的键来进行判断,返回的是字典中键对应的值。另外一种替代方法则用到了后文中才会提到的一些技术,例如嵌套函数和生成器。

def switch(value):
    fall = False
    def match(\*args):
        nonlocal value, fall
        if fall or not args:
            return True
        elif value in args:
            fall = True
            return True
        else:
            return False
    yield match

for case in switch('v'):
    if case('v'):
        pass
    if case('b'):
        pass

这种替代方法使用生成器,返回了用于判断的函数,实现了 switch/case 的连续判断分支结构。在这种方法中,对于match()中的逻辑可以根据需要进行更加详细的定义。

match结构

在Python 3.10版本中,一个功能更加强大的match语句结构被引入了进来。自此,Python中也就具备了类似与switch语句的结构,但是功能要更加强大。

match语句格式非常简单,其使用格式如下所示。

match 表达式:
    case 条件1:
        语句1
    case 条件2:
        语句2
    case 条件3 | 条件4 | 条件5:
        语句3
    case default:
        默认语句

match语句最简单的使用方式就是跟其他语言中的switch语句一样,在case语句中列举需要匹配的值即可。此时的match语句相当于多条if exp == value:的组合。如果想要达到在匹配多个值时都执行相同分支语句的效果,那么可以使用上面格式说明中的组合条件:case 条件 | 条件 | 条件:,这种形式的分支允许match语句匹配列举的任意条件。

Tip

match语句中的case分支不会向下贯通式的执行,每次仅会执行一个分支。

传统使用if/else结构分支语句的时候,在语句结构的最后往往会使用一个else语句块来执行没有任何匹配内容时的处理。在match语句中,这种默认匹配是采用case default:或者使用case _:的分支来声明的,这个默认分支通常都被放置在整个match语句结构的末尾。

match语句还支持对给定的表达式进行解构,例如以下示例中就解构了一个元组。

point = (2, 1)

match point:
    case (1, y):
        print(f"点在行1,列{y}上")
    case (x, 0):
        print(f"点在纵座标轴上,行{x}")
    case (x, y):
        print(f"点在行{x},列{y}上")
    case _:
        raise ValueError("给定值不是一个点坐标。")

除了可以解构元组以外,match语句还可以解构数据类,其解构的使用方法与解构元组基本一致,只是case分支要改用数据类的类名来进行解构操作指定,如果只需要解构其中一部分内容,还可以使用命名参数来获取指定字段的内容。

另外,对于上面这个示例来说,还可以使用更加Pythonic的方式,那就是使用if来执行额外的判断。改写一下上面这个示例变成了以下的样子。

point = (2, 1)

match point:
    case (x, y) if x == 1:
        print(f"点在行1,列{y}上")
    case (x, y) if y == 0:
        print(f"点在纵座标轴上,行{x}")
    case (x, y):
        print(f"点在行{x},列{y}上")
    case _:
        raise ValueError("给定值不是一个点坐标。")

在使用if进行额外判断的时候,要记住,match语句会首先捕获值,然后再执行if判断。

对于匹配枚举类中的成员,匹配的值必须书写连带枚举类名称的枚举成员全称,例如以下示例。

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

match color:
    case Color.RED:
        pass
    case Color.GREEN:
        pass
    case Color.BLUE:
        pass

在对枚举类成员进行匹配的时候,match语句中的分支应该尽量列举枚举中所有可能的值,避免出现空缺匹配的问题。