前端 Python 3.12

4. 控制流与函数

if/for、range、break-continue、for-else、match、函数参数与 *args/**kwargs、对照 JS 的 sleep 与 async/await、LEGB 作用域、lambda 与 PEP 8 速览。

4.1 if 语句

  • if / elif / else,无 switch;多分支用 elif 比层层嵌套更直。
  • 模式:把「快速失败 / 守卫子句」写在前面,主体缩进更少。
payload = {"role": "admin", "token": "abc"}

if payload.get("token") is None:
    print("缺少 token")
elif payload.get("role") != "admin":
    print("权限不足")
else:
    print("放行")

4.2 for 语句

  • 直接迭代可迭代对象;需要 (索引, 值) 时用 enumerate
  • 不要在遍历集合时修改被遍历对象;字典删键用副本迭代。
items = ["alpha", "beta", "gamma"]
for i, w in enumerate(items, start=1):
    print(i, w)

users = {"Hans": "inactive", "robin": "active", "gallahad": "active"}
for name, status in list(users.items()):  # list():快照,避免遍历时 resize
    if status == "inactive":
        del users[name]

4.3 range

  • 算术序列,左闭右开;惰性,省内存。
  • 反模式for i in range(len(xs)) 能用 enumerate 替换就别手写索引。
xs = ["a", "b", "c"]
for i, x in enumerate(xs):
    print(i, x)

# 只有在确实需要索引跳着走时,才用 range
for i in range(0, len(xs), 2):
    print(xs[i])

4.4 break / continue

  • break:离开最近一层循环。
  • continue:跳到本层循环的下一次迭代。
for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print(f"{n} = {x} * {n // x}")
            break

4.5 循环上的 else

  • else 挂在 for/while循环没有被 break 打断才执行。
  • 高频坑:把它当成 ifelse 会读错控制流。
for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print(n, "非质数")
            break
    else:
        print(n, "是质数")

4.6 pass

语法占位:空类、待实现函数、占位分支。

class Todo(Exception):
    pass

4.7 match(3.10+,结构化模式)

可做增强分支、解构、守卫条件(if);case _ 兜底。

def http_error(status: int) -> str:
    match status:
        case 400:
            return "请求错误"
        case 404:
            return "未找到"
        case 401 | 403:
            return "无权限"
        case _:
            return "其它"


match {"kind": "user", "id": 99}:
    case {"kind": "user", "id": user_id}:
        print("user", user_id)
    case _:
        print("unknown")

4.8 定义函数

  • def;无 return 等价返回 None
  • 首段文档字符串 → __doc__(可被 help()、工具链读取)。
def fib(n: int) -> None:
    """打印小于 n 的斐波那契数列"""
    a, b = 0, 1
    while a < n:
        print(a, end=" ")
        a, b = b, a + b
    print()


def fib2(n: int) -> list[int]:
    result: list[int] = []
    a, b = 0, 1
    while a < n:
        result.append(a)
        a, b = b, a + b
    return result

4.9 函数高级用法

默认参数(只算一次)

下面展示经典可变默认值坑:默认形参在函数定义时求值一次。

# 错误示范:默认 list 在定义时只创建一次,所有调用共享
def broken(acc: list[int] = []) -> list[int]:
    acc.append(1)
    return acc


print(broken())  # [1]
print(broken())  # [1, 1] —— 往往不符合直觉


def ok(acc: list[int] | None = None) -> list[int]:
    if acc is None:
        acc = []
    acc.append(1)
    return acc

关键字参数与顺序

先位置后关键字:parrot(1000, action="VOOOOOM")

仅位置 / 仅关键字(3.8+)

/ 前仅限位置;* 后仅限关键字。

def f(pos1: int, /, pos_or_kwd: int, *, kwd_only: int) -> None:
    print(pos1, pos_or_kwd, kwd_only)


f(1, 2, kwd_only=3)

*args**kwargs

def concat(*parts: str, sep: str = "/") -> str:
    return sep.join(parts)


print(concat("a", "b", "c", sep="."))

解包调用(对标展开数组 / 对象):

args = [3, 6]
list(range(*args))

d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}


def parrot(voltage: str, state: str = "a stiff", action: str = "voom") -> None:
    print("-- This parrot wouldn't", action, end=" ")
    print("if you put", voltage, "volts through it.", end=" ")
    print("E's", state, "!")


parrot(**d)

lambda

只适合极短回调;复杂逻辑请写 def(需要栈帧、文档字符串、类型注解时尤其如此)。

pairs = [(1, "one"), (2, "two"), (3, "three"), (4, "four")]
pairs.sort(key=lambda p: p[1])

注解不等于校验

def add(a: int, b: int) -> int:
    return a + b


print(add("x", "y"))  # 'xy' —— 运行期照样执行,除非你用第三方做校验

4.10 函数里直接 sleep

  • JavaScriptasync / await 主要是配合 Promise
  • Pythontime.sleep(seconds) 是标准库里的普通同步函数:让当前 OS 线程休眠指定秒数。
  • Python 也有 async / await(常见于 asyncio):那是另一套协程 / 事件循环;协程里推迟执行一般用 await asyncio.sleep(...),避免在里面长时间 time.sleep 卡住同一线程上的事件循环
import time

def demo_py() -> None:
    time.sleep(0.1)  # 同步阻塞当前线程;

4.11 作用域

解释器按 LEGB 从内到外找名字:Local(本地)→ Enclosing(外层函数)→ Global(模块全局)→ Built-in(内建)。需要在内层函数里改外层函数的变量时,用 nonlocal;改模块级名字用 global

命名空间

  • 函数def / lambda 体各自有一层局部作用域。
  • :类体在执行时也有局部命名空间,结束后名字多半变成类属性(方法会变成描述符等,细节见类章节)。
  • 推导式(3.x):列表 / 集合 / 字典推导里的「循环变量」有自己的局部作用域,不会泄漏到外层的函数或模块里。
  • 生成器表达式:类似推导式,有自己的作用域。

哪些不是独立作用域

  • if / elif / elsefor / whiletry / except / finallymatch / case 等分支与循环:它们为赋值语句新建一层作用域;在函数里写的 x = 1 仍是该函数的局部变量(除非声明了 global / nonlocal)。这也是「if 里定义的变量,分支外仍可见」的原因。
  • 模块顶层的缩进块:名字落在模块全局,不是「if 块专属的全局」。
if True:
    message = "still module/global or function local, not a new scope"


def demo() -> None:
    if True:
        a = 1
    print(a)  # 1 —— a 属于 demo 的局部作用域,不是 if 的子作用域


[x for x in range(3)]
# print(x)  # NameError:推导式里的 x 不泄漏(Python 3)

4.12 编码风格(PEP 8 摘录)

  1. 每层缩进 4 空格
  2. 一行尽量 ≤ 79(或团队用 88/100,交给 formatter)。
  3. 二元运算符两侧空格。
  4. 函数 / 变量:snake_case;类:CapWords
  5. 公共 API 写 docstring。

权威延伸4. More Control Flow Tools · PEP 8

On this page