会用@是中级Python底线;带参数装饰器能过滤掉"只会用不懂原理"的。
`@decorator\ndef f:...`等价于`f = decorator(f)`。decorator是任意接受callable返回callable的东西。
典型:`def deco(fn): def wrapper(*a, **kw): ...; return fn(*a, **kw); return wrapper`。
在wrapper上加`functools.wraps(fn)`保留__name__、__doc__、__wrapped__——否则help()和日志就乱了。
带参数装饰器:三层——最外层接参数、返回真正的装饰器。
类装饰器:__init__捕获fn、__call__让实例可调用。
装饰器就是任意"接受函数返回可调用"的东西。`@foo`在`def f()`上面等价于`f = foo(f)`。套路:外层函数里定义wrapper,wrapper在调用原函数前后加逻辑,返回wrapper。永远在wrapper上加`@functools.wraps(fn)`——否则__name__、__doc__、trace都会变成"wrapper"。`@retry(times=3)`要三层:retry(times)返回decorator(fn),decorator返回wrapper(*a, **kw)在其中循环捕获异常。带参数装饰器绕晕人的点:最外层拿到参数,它的返回值才是真正的装饰器。
@functools.lru_cache是内置好装饰器——零TTL内存记忆化。
异步函数要写异步感知的装饰器:wrapper必须async def并await fn(...)。
即答侠背了三层带参模板+functools.wraps位置正确。
被装饰函数的名字和docstring会丢,inspect/sphinx/调试器看到的都是wrapper。讨厌但不致命。
类装饰器用self存状态更清晰。函数装饰器更轻。功能上等价。