Python 装饰器:从语法糖到工业级用法
装饰器是什么?
装饰器的本质就一句话:接收一个函数,返回另一个函数的函数。
# 这两种写法完全等价
@my_decorator
def hello():
print("hello")
# 等价于:
def hello():
print("hello")
hello = my_decorator(hello)
@ 只是语法糖,不神秘。
最简装饰器
def my_decorator(func):
def wrapper(*args, **kwargs):
print("before")
result = func(*args, **kwargs) # 调用原函数
print("after")
return result
return wrapper
@my_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Python")
# before
# Hello, Python!
# after
*args, **kwargs 是标配 — 让 wrapper 能接受任意参数,透明传给原函数。
必须加 @functools.wraps
不加的话,被装饰的函数会”失去身份”:
import functools
def my_decorator(func):
@functools.wraps(func) # ← 必须加这个!
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@my_decorator
def hello():
"""这是 hello 函数"""
pass
print(hello.__name__) # hello(加了 wraps 才正确)
print(hello.__doc__) # 这是 hello 函数
带参数的装饰器
需要多套一层函数:
import functools
def repeat(n): # 最外层:接收参数
def decorator(func): # 中间层:接收函数
@functools.wraps(func)
def wrapper(*args, **kwargs): # 最内层:执行逻辑
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def say(msg):
print(msg)
say("hi")
# hi
# hi
# hi
10 个工业级装饰器场景
1. 计时器
import time
import functools
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} 耗时 {elapsed:.4f}s")
return result
return wrapper
@timer
def slow_func():
time.sleep(0.1)
2. 重试机制
import functools, time
def retry(max_attempts=3, delay=1.0):
def decorator(func):
@functools.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
time.sleep(delay)
print(f"第 {attempt+1} 次失败,重试...")
return wrapper
return decorator
@retry(max_attempts=3, delay=0.5)
def call_api():
# 可能失败的网络请求
...
3. 缓存(内置版)
from functools import lru_cache, cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# Python 3.9+,无限缓存
@cache
def expensive(n):
return n ** 2
4. 类型检查
import functools
from typing import get_type_hints
def type_check(func):
hints = get_type_hints(func)
@functools.wraps(func)
def wrapper(*args, **kwargs):
for arg_name, arg_val in zip(func.__code__.co_varnames, args):
if arg_name in hints:
expected = hints[arg_name]
if not isinstance(arg_val, expected):
raise TypeError(f"{arg_name} 需要 {expected.__name__},got {type(arg_val).__name__}")
return func(*args, **kwargs)
return wrapper
@type_check
def add(a: int, b: int) -> int:
return a + b
5. 权限控制
from functools import wraps
def require_auth(role="user"):
def decorator(func):
@wraps(func)
def wrapper(request, *args, **kwargs):
if not getattr(request, 'user', None):
raise PermissionError("未登录")
if request.user.role != role:
raise PermissionError(f"需要 {role} 权限")
return func(request, *args, **kwargs)
return wrapper
return decorator
@require_auth(role="admin")
def delete_user(request, user_id):
...
6. 日志记录
import logging
import functools
def log_calls(logger=None):
def decorator(func):
_logger = logger or logging.getLogger(func.__module__)
@functools.wraps(func)
def wrapper(*args, **kwargs):
_logger.info(f"调用 {func.__name__}({args}, {kwargs})")
result = func(*args, **kwargs)
_logger.info(f"{func.__name__} 返回 {result}")
return result
return wrapper
return decorator
7. 单例模式
def singleton(cls):
instances = {}
@functools.wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Config:
def __init__(self):
self.debug = False
类装饰器
用类实现装饰器,适合需要保存状态的场景:
import functools
class Counter:
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"调用次数:{self.count}")
return self.func(*args, **kwargs)
@Counter
def say_hello():
print("Hello!")
say_hello() # 调用次数:1
say_hello() # 调用次数:2
print(say_hello.count) # 2
装饰器叠加顺序
多个装饰器叠加时,从下往上执行:
@decorator_A # 最后执行(最外层)
@decorator_B # 先执行(最内层)
def func():
pass
# 等价于:
func = decorator_A(decorator_B(func))
知识卡片 📌
装饰器三件套:
1. *args, **kwargs — 透明传参
2. @functools.wraps — 保留函数元信息
3. return result — 别忘了返回值
带参数装饰器 = 3层嵌套函数
参数层 → 装饰器层 → wrapper 层
常用内置装饰器:
@property — 属性访问
@staticmethod — 静态方法
@classmethod — 类方法
@functools.lru_cache — 缓存
@dataclasses.dataclass — 数据类