04 控制流、函数与作用域
大约 3 分钟编程语言Python函数控制流装饰器
04 控制流、函数与作用域
控制流负责表达决策,函数负责定义边界。一个函数最好只承担一个明确职责,并暴露稳定的输入输出。
1. 条件与 match
def classify(code: int) -> str:
if code == 0:
return "ok"
if 400 <= code < 500:
return "client_error"
if 500 <= code < 600:
return "server_error"
return "unknown"match 适合结构化模式匹配,不适合替代所有 if:
def describe_event(event: dict) -> str:
match event:
case {"type": "http", "status": status} if status >= 500:
return f"http failure: {status}"
case {"type": "http", "status": status}:
return f"http response: {status}"
case {"type": name}:
return f"event: {name}"
case _:
return "unknown event"2. for、while 与 else
for attempt in range(1, 4):
if attempt == 2:
print("matched", attempt)
break
else:
print("not matched")循环的 else 在未执行 break 时运行。它能表达“搜索失败”分支,但团队不熟悉时可以用显式标志提升可读性。
3. 函数参数
def deploy(
service: str,
version: str,
*,
replicas: int = 2,
dry_run: bool = False,
) -> dict[str, object]:
return {
"service": service,
"version": version,
"replicas": replicas,
"dry_run": dry_run,
}参数顺序:
- 位置参数
- 默认参数
*args- 仅关键字参数
**kwargs
关键参数使用命名传递,可以降低调用点的语义歧义。
4. 作用域与闭包
def make_counter(start: int = 0):
current = start
def increment(step: int = 1) -> int:
nonlocal current
current += step
return current
return increment
counter = make_counter(10)
print(counter())
print(counter())查找顺序通常是局部、外层函数、模块全局、内置。尽量避免用 global 修改共享状态。
5. 装饰器
from functools import wraps
from time import perf_counter
from typing import Callable, ParamSpec, TypeVar
P = ParamSpec("P")
R = TypeVar("R")
def timed(function: Callable[P, R]) -> Callable[P, R]:
@wraps(function)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
started = perf_counter()
try:
return function(*args, **kwargs)
finally:
elapsed = perf_counter() - started
print(f"{function.__name__} elapsed={elapsed:.6f}s")
return wrapper
@timed
def add(left: int, right: int) -> int:
return left + rightfunctools.wraps 会保留原函数元数据。装饰器不要吞掉异常,除非它有明确的恢复或审计责任。
6. 函数式工具
from functools import lru_cache, partial
@lru_cache(maxsize=128)
def parse_port(name: str) -> int:
return int(name.removeprefix("port-"))
load_http = partial(parse_port, "port-80")
print(load_http())缓存只适合纯函数或结果稳定且参数可哈希的函数。缓存认证、动态配置或频繁变化的远端数据前,要设计失效策略。
7. 上下文管理器
from contextlib import contextmanager
from time import perf_counter
@contextmanager
def timer(name: str):
started = perf_counter()
try:
yield
finally:
print(name, round(perf_counter() - started, 6))
with timer("work"):
sum(range(1000))上下文管理器适合表达“必须成对出现”的操作:打开和关闭、加锁和释放、进入和回滚、开始和结束。
8. 常见陷阱
- 函数同时修改输入、写文件并返回状态,调用者难以预测副作用。
- 装饰器缺少
wraps,导致日志、签名和调试信息失真。 - 闭包捕获循环变量后,所有函数看到最后一次值。
- 用异常控制正常业务流程,导致成本和可读性变差。
- 递归没有明确终止条件或深度限制。
