Decorator (装饰器)
一句话:装饰器就是一个函数,用来给另外一个函数增强功能,但不修改原代码。
你写一个函数 f(),装饰器 @decorator 可以在它前后执行代码,例如增加日志、权限检查、计时功能等。
def my_decorator(func):
def wrapper():
print("执行前")
func()
print("执行后")
return wrapper
@my_decorator
def say_hello():
print("Hello")
say_hello()
输出:
执行前
Hello
执行后
解释:
@my_decorator等价于say_hello = my_decorator(say_hello)wrapper()替代了原函数,但内部仍调用原函数,所以功能被“包装”了。
适用于大多数真实场景。
from functools import wraps
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
print("before:", fn.__name__)
result = fn(*args, **kwargs)
print("after:", fn.__name__)
return result
return wrapper
记住:wrapper 必须接收 *args, **kwargs
这个版本能装饰任意函数。
@wraps(fn) 作用是保留原函数信息,否则 fn.__name__ 会变成 “wrapper”。
def log(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
print(f"[LOG] 调用函数 {fn.__name__}")
return fn(*args, **kwargs)
return wrapper
import time
def timer(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
start = time.time()
result = fn(*args, **kwargs)
print("耗时:", time.time() - start)
return result
return wrapper
def require_admin(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
if not kwargs.get("is_admin"):
raise PermissionError("无权限")
return fn(*args, **kwargs)
return wrapper
def cache(fn):
memo = {}
@wraps(fn)
def wrapper(n):
if n not in memo:
memo[n] = fn(n)
return memo[n]
return wrapper
想要写成 @decorator(arg) 的形式?
需要 “三层函数”:
def repeat(times):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
for _ in range(times):
fn(*args, **kwargs)
return wrapper
return decorator
使用:
@repeat(3)
def hi():
print("hi")
装饰器不止可以是函数,也可以是类。
class Decorator:
def __init__(self, fn):
self.fn = fn
def __call__(self, *args, **kwargs):
print("before")
return self.fn(*args, **kwargs)
@Decorator
def hello():
print("hello")
@decorator_a
@decorator_b
def f():
pass
等价于
f = decorator_a(decorator_b(f))
下方装饰器先执行。
- 装饰器 = 在不改原函数的前提下,额外添加功能
- 核心公式:
fn = decorator(fn) - 必备语法:
wrapper(*args, **kwargs) - 最好加:
from functools import wraps - 带参数 = 三层函数
- 多个装饰器 = 从下往上套
以下是 最常见、最实用、能直接复制使用的 20 个 Python 装饰器实战示例。
所有示例都经过精简,可直接用在真实项目中(包括你常用的 FastAPI 项目)。
from functools import wraps
def log(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
print(f"[LOG] 调用 {fn.__name__}")
return fn(*args, **kwargs)
return wrapper
import time
from functools import wraps
def timer(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
start = time.time()
result = fn(*args, **kwargs)
print(f"耗时 {time.time() - start:.4f}s")
return result
return wrapper
from functools import wraps
def catch_error(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
try:
return fn(*args, **kwargs)
except Exception as e:
print(f"[ERROR] {fn.__name__}: {e}")
return None
return wrapper
def type_check(*types):
def decorator(fn):
@wraps(fn)
def wrapper(*args):
for a, t in zip(args, types):
assert isinstance(a, t), f"{a} must be {t}"
return fn(*args)
return wrapper
return decorator
def cache(fn):
memo = {}
@wraps(fn)
def wrapper(*args):
if args not in memo:
memo[args] = fn(*args)
return memo[args]
return wrapper
def limit_times(n):
def decorator(fn):
count = 0
@wraps(fn)
def wrapper(*args, **kwargs):
nonlocal count
if count >= n:
print("已超过允许执行次数")
return
count += 1
return fn(*args, **kwargs)
return wrapper
return decorator
import time
from functools import wraps
def retry(times=3, delay=1):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
for i in range(times):
try:
return fn(*args, **kwargs)
except Exception:
time.sleep(delay)
raise
return wrapper
return decorator
def ensure_type(t):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
return t(fn(*args, **kwargs))
return wrapper
return decorator
def require_admin(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
if not kwargs.get('is_admin'):
raise PermissionError("No permission")
return fn(*args, **kwargs)
return wrapper
def trace(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
print(f"{fn.__name__} 入参: {args}, {kwargs}")
result = fn(*args, **kwargs)
print(f"{fn.__name__} 返回: {result}")
return result
return wrapper
def lazy(fn):
cached = {}
@wraps(fn)
def wrapper(*args, **kwargs):
if 'value' not in cached:
cached['value'] = fn(*args, **kwargs)
return cached['value']
return wrapper
def once(fn):
done = False
result = None
@wraps(fn)
def wrapper(*args, **kwargs):
nonlocal done, result
if not done:
result = fn(*args, **kwargs)
done = True
return result
return wrapper
import asyncio
from functools import wraps
def async_log(fn):
@wraps(fn)
async def wrapper(*args, **kwargs):
print(f"【ASYNC LOG】调用 {fn.__name__}")
return await fn(*args, **kwargs)
return wrapper
import time
def throttle(interval):
def decorator(fn):
last = 0
@wraps(fn)
def wrapper(*args, **kwargs):
nonlocal last
now = time.time()
if now - last > interval:
last = now
return fn(*args, **kwargs)
return wrapper
return decorator
import time
def debounce(wait):
def decorator(fn):
last = 0
@wraps(fn)
def wrapper(*args, **kwargs):
nonlocal last
last = time.time()
time.sleep(wait)
if time.time() - last >= wait:
return fn(*args, **kwargs)
return wrapper
return decorator
from functools import wraps
from threading import Lock
def with_lock(lock: Lock):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
with lock:
return fn(*args, **kwargs)
return wrapper
return decorator
def db_transaction(fn):
@wraps(fn)
def wrapper(db, *args, **kwargs):
try:
result = fn(db, *args, **kwargs)
db.commit()
return result
except:
db.rollback()
raise
return wrapper
import os
from functools import wraps
def cd(path):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
old = os.getcwd()
os.chdir(path)
try:
return fn(*args, **kwargs)
finally:
os.chdir(old)
return wrapper
return decorator
def retry_log(times=3):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
for i in range(1, times+1):
try:
return fn(*args, **kwargs)
except Exception as e:
print(f"重试 {i}/{times}: {e}")
raise
return wrapper
return decorator
def lazy_property(fn):
attr = "_lazy_" + fn.__name__
@property
@wraps(fn)
def wrapper(self):
if not hasattr(self, attr):
setattr(self, attr, fn(self))
return getattr(self, attr)
return wrapper
from functools import wraps
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
# 前置逻辑
result = fn(*args, **kwargs)
# 后置逻辑
return result
return wrapper