当前位置:首页>python>Python多个装饰器——叠加使用,增强组合

Python多个装饰器——叠加使用,增强组合

  • 2026-10-11 08:28:20
Python多个装饰器——叠加使用,增强组合

一、什么是多个装饰器?

多个装饰器是指在一个函数上同时应用多个装饰器,每个装饰器为函数添加不同的功能。Python 允许将多个装饰器叠加使用,实现功能的组合与叠加。

defdecorator_a(func):
defwrapper(*args, **kwargs):
print("A 开始")
        result = func(*args, **kwargs)
print("A 结束")
return result
return wrapper

defdecorator_b(func):
defwrapper(*args, **kwargs):
print("B 开始")
        result = func(*args, **kwargs)
print("B 结束")
return result
return wrapper

@decorator_a
@decorator_b
defsay_hello():
print("Hello!")

say_hello()

输出:

A 开始
B 开始
Hello!
B 结束
A 结束

二、多个装饰器的执行顺序

多个装饰器的执行顺序遵循从下往上装饰,从上往下执行的规则。

2.1 装饰顺序(定义时)

装饰器在函数定义时从最靠近函数的开始向外应用:

@decorator_a
@decorator_b
@decorator_c
deffunc():
pass

# 等价于:
func = decorator_a(decorator_b(decorator_c(func)))

2.2 执行顺序(调用时)

当调用被装饰的函数时,执行顺序是从上往下:

defdecorator_one(func):
print("装饰器 one 被调用")
defwrapper(*args, **kwargs):
print("one 开始执行")
        result = func(*args, **kwargs)
print("one 结束执行")
return result
return wrapper

defdecorator_two(func):
print("装饰器 two 被调用")
defwrapper(*args, **kwargs):
print("two 开始执行")
        result = func(*args, **kwargs)
print("two 结束执行")
return result
return wrapper

@decorator_one
@decorator_two
defmy_func():
print("原函数执行")

print("--- 开始调用 ---")
my_func()

输出:

装饰器 two 被调用
装饰器 one 被调用
--- 开始调用 ---
one 开始执行
two 开始执行
原函数执行
two 结束执行
one 结束执行

三、装饰器应用顺序分析图

@decorator_a
@decorator_b
@decorator_c
def func():
    pass

装饰阶段(定义时):

decorator_c(func)   →  wrapper_c
decorator_b(wrapper_c) → wrapper_b
decorator_a(wrapper_b) → wrapper_a
最终 func = wrapper_a

执行阶段(调用时):

wrapper_a() 开始
    ↓
wrapper_b() 开始
    ↓
wrapper_c() 开始
    ↓
原函数 func()
    ↓
wrapper_c() 结束
    ↓
wrapper_b() 结束
    ↓
wrapper_a() 结束

四、多个装饰器的常见应用场景

4.1 日志 + 计时

import time
from functools import wraps

deflog(func):
    @wraps(func)
defwrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
return func(*args, **kwargs)
return wrapper

deftimer(func):
    @wraps(func)
defwrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
print(f"{func.__name__} 耗时: {end - start:.4f}秒")
return result
return wrapper

@log
@timer
defslow_function():
    time.sleep(1)
return"完成"

print(slow_function())

4.2 缓存 + 重试

from functools import wraps
import time

defcache(func):
"""缓存装饰器"""
    cache_dict = {}
    @wraps(func)
defwrapper(*args):
if args in cache_dict:
print(f"缓存命中: {args}")
return cache_dict[args]
print(f"计算: {args}")
        result = func(*args)
        cache_dict[args] = result
return result
return wrapper

defretry(max_attempts=3, delay=1):
"""重试装饰器"""
defdecorator(func):
        @wraps(func)
defwrapper(*args, **kwargs):
for attempt inrange(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"第 {attempt} 次尝试失败: {e}")
if attempt == max_attempts:
raise
                    time.sleep(delay)
returnNone
return wrapper
return decorator

@cache
@retry(max_attempts=3)
defunstable_calc(n):
import random
if random.random() < 0.5:
raise ValueError("随机失败")
return n * n

print(unstable_calc(5))

4.3 权限验证 + 日志

from functools import wraps

defrequire_permission(permission):
"""权限验证装饰器"""
defdecorator(func):
        @wraps(func)
defwrapper(user, *args, **kwargs):
ifhasattr(user, 'permissions') and permission in user.permissions:
return func(user, *args, **kwargs)
raise PermissionError(f"用户 {user.name} 缺少权限: {permission}")
return wrapper
return decorator

defaudit_log(func):
"""审计日志装饰器"""
    @wraps(func)
defwrapper(user, *args, **kwargs):
print(f"[AUDIT] {user.name} 调用了 {func.__name__}")
return func(user, *args, **kwargs)
return wrapper

classUser:
def__init__(self, name, permissions):
self.name = name
self.permissions = permissions

@audit_log
@require_permission("delete")
defdelete_file(user, filename):
print(f"{user.name} 删除了文件: {filename}")

admin = User("张三", ["delete", "read"])
normal = User("李四", ["read"])

delete_file(admin, "test.txt")   # 成功
# delete_file(normal, "test.txt")  # PermissionError

五、装饰器顺序的影响

装饰器的顺序非常重要,不同顺序会产生不同的效果。

5.1 缓存应该在重试之后还是之前?

# 场景1:缓存在外层(先检查缓存,再重试)
@cache
@retry
deffunc(): ...

# 场景2:重试在外层(先重试,再缓存)
@retry
@cache
deffunc(): ...
  • • 场景1:缓存失败的结果也会被缓存,重试逻辑可能不会执行。
  • • 场景2:每次缓存未命中时都会执行重试逻辑,但成功结果会被缓存。

选择原则:

  • • 如果希望缓存的是最终成功的结果(包括重试后的),重试应该在内层。
  • • 如果希望每次都重试,但缓存的是成功结果,缓存应该在内层。

5.2 计时应该在日志外层还是内层?

# 计时包含日志输出时间
@timer
@log
deffunc(): ...

# 日志包含计时时间
@log
@timer
deffunc(): ...

前者记录的是函数+日志的总时间,后者记录的是纯函数执行时间。


六、类装饰器的叠加

类装饰器也可以叠加使用。

classLogDecorator:
def__init__(self, func):
self.func = func

def__call__(self, *args, **kwargs):
print(f"Log: 调用 {self.func.__name__}")
returnself.func(*args, **kwargs)

classTimerDecorator:
def__init__(self, func):
self.func = func

def__call__(self, *args, **kwargs):
import time
        start = time.time()
        result = self.func(*args, **kwargs)
        end = time.time()
print(f"Timer: {self.func.__name__} 耗时 {end-start:.4f}s")
return result

@LogDecorator
@TimerDecorator
defprocess_data():
print("处理数据...")
return"完成"

process_data()

七、使用 functools.partial 简化多层装饰器

当需要多次应用相同装饰器时,可以使用 partial 简化。

from functools import partial

defrepeat(times):
defdecorator(func):
defwrapper(*args, **kwargs):
for _ inrange(times):
                result = func(*args, **kwargs)
return result
return wrapper
return decorator

# 手动创建多个相同装饰器
@repeat(2)
@repeat(3)
@repeat(5)
defgreet():
print("Hello")

greet()   # 会执行 2*3*5 = 30 次

# 更清晰的写法
repeat_30 = partial(repeat, 30)

@repeat_30
defgreet2():
print("Hello")

八、注意事项

  1. 1. 顺序敏感:多个装饰器的顺序会影响最终行为,需要根据需求仔细安排。
  2. 2. 元信息保留:每个装饰器都应该使用 @wraps(func),否则最外层装饰器会暴露内层装饰器的元信息。
  3. 3. 性能影响:每个装饰器都会增加函数调用开销,过多装饰器可能影响性能。
  4. 4. 调试困难:多层装饰器会让堆栈跟踪变得复杂,使用 @wraps 可以改善。
  5. 5. 参数传递:所有装饰器的 wrapper 函数都需要正确处理 *args, **kwargs,以确保参数能正确传递给原函数。

九、调试多个装饰器

from functools import wraps

defdebug_decorator(name):
"""用于调试的装饰器,显示装饰器应用顺序"""
defdecorator(func):
print(f"正在应用装饰器: {name} 到函数 {func.__name__}")
        @wraps(func)
defwrapper(*args, **kwargs):
print(f"执行装饰器: {name} 开始")
            result = func(*args, **kwargs)
print(f"执行装饰器: {name} 结束")
return result
return wrapper
return decorator

@debug_decorator("A")
@debug_decorator("B")
@debug_decorator("C")
defmy_func():
print("原函数执行")

print("--- 调用函数 ---")
my_func()

输出:

正在应用装饰器: C 到函数 my_func
正在应用装饰器: B 到函数 wrapper
正在应用装饰器: A 到函数 wrapper
--- 调用函数 ---
执行装饰器: A 开始
执行装饰器: B 开始
执行装饰器: C 开始
原函数执行
执行装饰器: C 结束
执行装饰器: B 结束
执行装饰器: A 结束

十、总结

要点
说明
装饰顺序
从下往上(靠近函数的先装饰)
执行顺序
从上往下(远离函数的先执行)
等价形式
func = decorator1(decorator2(decorator3(func)))
常见组合
日志+计时、缓存+重试、权限+审计
注意事项
顺序敏感、保留元信息、注意性能

核心理解:

  • • 多个装饰器像洋葱一样层层包裹,调用时从外向内,返回时从内向外。
  • • 装饰器顺序决定了功能的组合方式,不同顺序产生不同效果。
  • • 每个装饰器都应使用 @functools.wraps 保留函数元信息。
  • • 合理组合多个装饰器可以实现功能的模块化和复用。

掌握多个装饰器的叠加使用,可以让你用声明式的方式组合各种功能,写出更加清晰、可维护的代码。

最新文章

随机文章