#Python 装饰器
装饰器(decorator)可以在不修改原函数代码的前提下,动态扩展函数或类的功能。本质上装饰器就是一个函数——接收一个函数作为参数,返回一个新函数。
#一、基本概念
def my_decorator(func):
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# 函数执行前
# Hello!
# 函数执行后@my_decorator 是语法糖,等价于:
def say_hello():
print("Hello!")
say_hello = my_decorator(say_hello)所以调用 say_hello() 时,实际执行的是 wrapper(),wrapper 在原函数前后插入了额外逻辑。
#二、带参数的装饰器
如果原函数有参数,wrapper 需要用 *args, **kwargs 接收所有参数:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("执行前")
result = func(*args, **kwargs)
print("执行后")
return result
return wrapper
@my_decorator
def greet(name, msg="Hello"):
print(f"{msg}, {name}!")
greet("Alice", msg="Hi")
# 执行前
# Hi, Alice!
# 执行后#三、带参数的装饰器(装饰器工厂)
当装饰器本身也需要接收参数时,需要在外面再套一层函数,变成三层嵌套:
def repeat(num_times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello()
# Hello!
# Hello!
# Hello!执行过程:@repeat(3) → 先调用 repeat(3) 返回 decorator → decorator 接收 say_hello 返回 wrapper → say_hello 被替换为 wrapper。
#四、实战:记录函数执行时间
import time
def runtime(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"函数 {func.__name__} 运行时间:{end - start:.4f}秒")
return result
return wrapper
@runtime
def slow_function():
time.sleep(2)
return "done"
slow_function()
# 函数 slow_function 运行时间:2.0001秒#五、多个装饰器堆叠
多个装饰器从下到上依次应用,从外到内依次执行:
def decorator1(func):
def wrapper(*args, **kwargs):
print("Decorator 1 开始")
result = func(*args, **kwargs)
print("Decorator 1 结束")
return result
return wrapper
def decorator2(func):
def wrapper(*args, **kwargs):
print("Decorator 2 开始")
result = func(*args, **kwargs)
print("Decorator 2 结束")
return result
return wrapper
@decorator1
@decorator2
def say_hello():
print("Hello!")
say_hello()
# Decorator 1 开始
# Decorator 2 开始
# Hello!
# Decorator 2 结束
# Decorator 1 结束@decorator1 和 @decorator2 的执行顺序相当于 decorator1(decorator2(say_hello)),所以 decorator1 在最外层,最先执行。
#六、保留原函数信息 functools.wraps
装饰器会替换原函数,导致原函数的 __name__、__doc__ 等属性丢失。用 functools.wraps 修复:
from functools import wraps
def my_decorator(func):
@wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
"""wrapper 的文档"""
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello():
"""这是 say_hello 的文档"""
print("Hello!")
print(say_hello.__name__) # say_hello(不加 wraps 会变成 wrapper)
print(say_hello.__doc__) # 这是 say_hello 的文档#七、常见应用场景
#7.1 计时装饰器(通用版)
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start
print(f"{func.__name__} 耗时 {elapsed:.4f}s")
return result
return wrapper
@timer
def add(a, b):
time.sleep(1)
return a + b
print(add(1, 2)) # 3#7.2 日志装饰器
from functools import wraps
def log(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__},参数: {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} 返回: {result}")
return result
return wrapper
@log
def multiply(a, b):
return a * b
multiply(3, 4)
# 调用 multiply,参数: (3, 4), {}
# multiply 返回: 12#7.3 缓存装饰器(函数结果缓存)
from functools import wraps, lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(30)) # 832040(第一次计算后缓存结果)
print(fibonacci.cache_info()) # 查看缓存命中率自己实现一个简易缓存装饰器:
from functools import wraps
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 fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(100)) # 354224848179261915075#7.4 权限校验装饰器
from functools import wraps
def require_role(role):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
user_role = kwargs.get("role") or (args[0] if args else None)
if user_role != role:
print(f"权限不足,需要 {role} 角色")
return None
return func(*args, **kwargs)
return wrapper
return decorator
@require_role("admin")
def delete_user(username):
print(f"删除用户 {username}")
delete_user("alice") # 权限不足,需要 admin 角色
delete_user("alice", role="admin") # 删除用户 alice#7.5 重试装饰器
import time
from functools import wraps
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:
print(f"第 {attempt + 1} 次尝试失败: {e}")
if attempt < max_attempts - 1:
time.sleep(delay)
print(f"重试 {max_attempts} 次后仍然失败")
return None
return wrapper
return decorator
@retry(max_attempts=3, delay=2)
def unstable_function():
import random
if random.random() < 0.7:
raise ConnectionError("网络超时")
return "成功"
print(unstable_function())#八、类装饰器
除了函数,装饰器也可以用类来实现。
#8.1 类作为装饰器
类中定义 __call__ 方法,让实例可以像函数一样被调用:
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"{self.func.__name__} 被调用了 {self.count} 次")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello() # say_hello 被调用了 1 次 → Hello!
say_hello() # say_hello 被调用了 2 次 → Hello!#8.2 用类装饰器实现单例模式
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("初始化数据库")
db1 = Database() # 初始化数据库
db2 = Database() # 没有输出,直接复用
print(db1 is db2) # True#九、内置装饰器
Python 自带了一些常用的装饰器:
class MyClass:
def __init__(self):
self._name = ""
@staticmethod # 静态方法 — 不需要 self/cls,类和实例都能调用
def static_func():
print("静态方法")
@classmethod # 类方法 — 第一个参数是 cls(类本身)
def class_func(cls):
print(f"类方法,类名: {cls.__name__}")
@property # 属性装饰器 — 方法变属性,支持 getter/setter
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
obj = MyClass()
obj.static_func() # 静态方法
MyClass.class_func() # 类方法,类名: MyClass
obj.name = "Alice"
print(obj.name) # Alice#十、用在类上
装饰器也可以修饰整个类,常用于自动注册、单例等场景:
registry = {}
def register(cls):
registry[cls.__name__] = cls
return cls
@register
class Dog:
pass
@register
class Cat:
pass
print(registry) # {'Dog': <class '__main__.Dog'>, 'Cat': <class '__main__.Cat'>}在 Flask/Django 等框架中大量使用这种模式来实现路由注册。

