Python 装饰器
函数是一等公民
def hello():
return "Hello!"
# 函数可以赋值给变量
greet = hello
print(greet()) # Hello!
# 函数可以作为参数
def call_twice(fn):
fn()
fn()
# 函数可以定义在函数内部
def outer():
def inner():
return "inner"
return inner() # 返回 inner 的调用结果
闭包 → 装饰器
# 闭包 = 函数 + 捕获的外部变量
def make_multiplier(factor):
def multiply(x):
return x * factor # factor 被"捕获"
return multiply # 返回内部函数
double = make_multiplier(2)
print(double(5)) # 10
装饰器就是语法糖:
# 下面两段等价:
@decorator
def foo(): ...
def foo(): ...
foo = decorator(foo)
基础装饰器
import time
from functools import wraps
def timer(func):
@wraps(func) # 保留原函数的 __name__ 和 __doc__
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start
print(f"{func.__name__} took {elapsed:.4f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(0.5)
return "done"
slow_function()
# 输出: slow_function took 0.5001s
@wraps 不可省略 — 不加的话 slow_function.__name__ 会变成 "wrapper"。
带参数的装饰器
def repeat(n):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello() # 打印 3 次
# 等价于: say_hello = repeat(3)(say_hello)
实战场景
场景 1:缓存(memoize)
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fib(n):
if n < 2: return n
return fib(n-1) + fib(n-2)
print(fib(100)) # 毫秒级
场景 2:权限检查
def require_auth(role):
def decorator(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if user.get("role") != role:
raise PermissionError(f"Need {role}")
return func(user, *args, **kwargs)
return wrapper
return decorator
@require_auth("admin")
def delete_user(admin, target_id):
print(f"Deleted {target_id}")
场景 3:重试
def retry(max_attempts=3, delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
print(f"Retry {attempt+1}/{max_attempts}: {e}")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=3, delay=2)
def unstable_api_call():
...
场景 4:日志
def log_calls(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"CALL {func.__name__}({args}, {kwargs})")
result = func(*args, **kwargs)
print(f"RETURN {func.__name__} → {result!r}")
return result
return wrapper
类装饰器
class Singleton:
def __init__(self, cls):
self.cls = cls
self.instance = None
def __call__(self, *args, **kwargs):
if self.instance is None:
self.instance = self.cls(*args, **kwargs)
return self.instance
@Singleton
class Database:
def __init__(self):
print("Init DB")
db1 = Database() # Init DB (只打印一次)
db2 = Database() # 返回同一个实例
print(db1 is db2) # True