# 流程控制

  • Python 提供了以下几种语法,来控制程序的运行流程:
    • 选择结构
      • if
      • match-case
    • 循环结构
      • for
      • while

# if

  • if 语句用于条件判断,最简单的格式如下:

    if condition:
        statement_block
    
    • 如果 condition 条件表达式结果为 True ,则执行 statement_block 语句块。否则,跳过 statement_block 。
    • 例:
      condition = 1 > 0
      if condition:
          statement_block
      
    • 除了简单的比较运算,condition 还可以是 int、str、函数等形式。执行 if condition 相当于执行 if bool(condition) ,总是会得到一个 True 或 False 值。
  • 可选加上 else 语句,表示条件表达式为 False 时,执行什么操作。如下:

    if condition:
        statement_block
    else:
        statement_block
    
  • 可选加上任意个 elif 语句,从而依次判断多个条件表达式。如下:

    if condition:
        statement_block
    elif condition:
        statement_block
    elif condition:
        statement_block
    
    • 如果第一个 condition 结果为 False ,则检查第二个 condition 。如果第二个 condition 结果也为 False ,则检查第三个 condition ,以此类推。
    • 如果所有 condition 结果都为 False ,则执行 else 语句下的 statement_block 。不过也可以省略 else 语句。
    • 如果某个 condition 结果都为 True ,则执行对应的 statement_block ,并跳过后续所有 elif、else 。
    • 注意 if、elif、else 语句的末尾,都要加上冒号 :

# match-case

  • match-case 语句用于模式匹配。格式如下:

    match <expression>
        case <pattern>:
            <block>
        case <pattern>:
            <block>
        ...
    
    • 执行时,会从上到下依次将 expression 与每个 case pattern 比较。如果匹配,则执行该 case block ,并结束 match-case 语句块。
  • 例:

    match 1:
        case 1:           # Literal Patterns :如果 pattern 为普通数据类型,则当 expression == pattern 时,视作匹配
            pass
        case 1:           # 允许声明重复的 case pattern
            pass
        case {1:''}:      # Mapping Patterns :如果 pattern 为 dict 类型,则当 pattern 包含于 expression 时,视作匹配
            print(1)
        case x:           # Capture Patterns :如果 pattern 为一个标识符,则视作变量,会匹配一个任意值,并赋值(称为 bind )。该变量在 match-case 语句块之后依然存在
            print(x)
    
        case 1 | 'A':             # 可以用 | 连接多个 pattern ,分别尝试匹配
            pass
        case [1, x, y] | (x, y):  # 用 | 连接的每个 pattern 中,必须使用相同数量、名称的变量,否则抛出异常:SyntaxError: alternative patterns bind different names
            pass
    
        case 1 as x:              # 可以用关键字 as 将匹配结果赋值给一个变量
            print(x)
        case [1, (2 | 3) as x]:
            pass
    
        case [1, x] if x > 0:     # 可以添加 if 条件。先判断 case pattern 是否匹配,如果匹配则绑定变量,再判断 if 条件是否成立,如果成立才执行 case block
            print(x)
        case [1, *args]:          # 支持使用 * 元组参数,匹配任意个值
            print(*args)
        case {1: _, **kwargs}:    # pattern 为 dict 类型时,支持使用 ** 字典参数
            print(kwargs)
    
        case _:                   # Wildcard Pattern :如果 pattern 只是一个 _ ,则可以匹配任意值,但不会绑定赋值给 _ 。如果 pattern 不止包含 _ ,则当作 Capture Patterns 处理
            pass
    
  • Class Patterns :如果 pattern 为一个对象,则先比较 expression、pattern 是否属于同一 class ,再比较实参列表是否相同,如果相同才算匹配。

    • pattern 不能是一个类名,否则会当作 Capture Patterns 处理。
      >>> match 1:
      ...     case str:         # 这里会把 expression 赋值给标识符 str
      ...         print(str)
      ...
      1
      
    • 内置类已经实现了 __match_args__ ,可以直接匹配。
      >>> match 1:
      ...     case int(1):
      ...         print(1)
      ...
      1
      
    • 比较实参列表时,字典参数会直接比较,而位置参数会根据 __match_args__ 转换成字典参数再比较。
      >>> class Point:
      ...     __match_args__ = ('x', 'y')
      ...     def __init__(self, x=0, y=0):
      ...         self.x = x
      ...         self.y = y
      ...
      >>>
      >>> match Point():
      ...     case Point():
      ...         print(1)
      ...
      1
      >>> match Point(1, 2):
      ...     case Point(1, y=2):
      ...         print(1)
      ...
      1
      
      • __match_args__ 取值为 tuple 类型。如果未定义,则在 case 比较时会抛出异常:TypeError: Point() accepts 0 positional sub-patterns (1 given)

# for

  • C 语言中,for 语句的功能是,执行指定次数的循环。例如:for(i; i<3; i++)

  • Python 语言中,for 语句的功能不同,是迭代(或者说遍历)一个对象中的所有元素。每获取一个元素,就执行一次 for 语句的循环体 statement_block 。

    • 目标对象必须属于可迭代对象。
    • 一般格式如下:
      for item in iterable:
          statement_block
      
    • 例:
      for i in 'hello':
          print(i)
      
      for _ in range(4):  # 如果不想遍历对象,只想执行指定次数的循环,则可以用 range() 函数创建一个指定长度的迭代器
          print('hello')
      
    • 每次迭代得到的一个元素,如果也属于可迭代对象,则可以 unpack 拆开成 n 个值,同时赋值给 n 个变量。例:
      >>> for x,y in ['ab', 'cd']:
      ...     print(x,y)
      ...
      a b
      c d
      
  • 如果目标对象,包含可执行代码(比如调用一个函数),则只会执行一次。例如:

    >>> def fun1():
    ...     print('this is fun1()')
    ...     return 'hi'
    ...
    >>> for i in fun1():
    ...     print(i)
    ...
    this is fun1()    # 函数只执行了一次
    h
    i
    

    相当于:

    _ = fun1()
    for i in _:
        print(i)
    
  • 迭代的过程中,目标对象包含的元素不能变化,否则会引发异常:

    >>> dic = {'k1': 1, 'k2': 2}
    >>> for k in dic.keys():
    ...     dic.pop(k)
    ...
    1
    RuntimeError: dictionary changed size during iteration
    >>> d
    {'k2': 10}
    

    为了解决该问题,可以先快速迭代一次,暂存所有元素,然后才慢慢迭代:

    >>> dic = {'k1': 1, 'k2': 2}
    >>> _ = list(dic.keys())
    >>> for k in _:
    ...     dic.pop(k)
    ...
    1
    2
    

# while

  • while 语句的功能是:只要满足指定条件,则反复执行一个循环。

    • 一般格式如下:
      while condition:
          statement_block
      
    • 运行流程:第一步,检查 condition 的值,
      • 如果为 True ,则执行一遍 statement_block 。执行之后,回到第一步,再次检查 condition 的值。
      • 如果为 False ,则结束 while 语句块。
  • Python 的 for、while 循环语句中,

    • 可以用关键字 break ,立即结束当前的 for 或 while 循环。
    • 可以用关键字 continue ,跳到下一次循环。
    • 可以附加 else 语句块。如果 for 或 while 循环是正常结束的(因为不满足循环条件而结束),则会执行 else 语句块。如果提前结束循环(因为 break、抛出异常等原因),则不会执行 else 语句块。例:
      >>> for i in range(3):
      ...     print(i)
      ... else:
      ...     print('else')
      ...
      0
      1
      2
      else