Python中的装饰器参数传递与多层装饰器执行顺序
字数 889 2025-11-10 20:11:39

Python中的装饰器参数传递与多层装饰器执行顺序

1. 问题描述

装饰器是Python中用于修改或增强函数行为的工具。但当装饰器本身需要参数,或多个装饰器同时修饰一个函数时,其执行顺序和参数传递可能令人困惑。例如:

@decorator1
@decorator2
def my_func():
    pass

或带参数的装饰器:

@decorator_factory(arg1, arg2)
def my_func():
    pass

面试常问:装饰器的嵌套顺序如何影响函数行为?带参数的装饰器如何实现?


2. 装饰器基础回顾

装饰器本质是接受函数作为参数的高阶函数。例如:

def simple_decorator(func):
    def wrapper():
        print("Before function call")
        func()
        print("After function call")
    return wrapper

@simple_decorator
def greet():
    print("Hello!")

# 调用 greet() 等价于 simple_decorator(greet)()

输出:

Before function call
Hello!
After function call

3. 带参数的装饰器

若装饰器需要接收外部参数(如@delay(seconds=2)),需构造一个装饰器工厂函数,返回真正的装饰器:

def repeat(n):  # 装饰器工厂函数
    def actual_decorator(func):  # 真正的装饰器
        def wrapper(*args, **kwargs):
            for _ in range(n):
                func(*args, **kwargs)
        return wrapper
    return actual_decorator

@repeat(n=3)
def say_hello():
    print("Hello")

say_hello()  # 打印3次 "Hello"

执行过程分析

  1. @repeat(n=3) 先调用 repeat(3),返回 actual_decorator
  2. actual_decorator(say_hello) 返回 wrapper 函数;
  3. 调用 say_hello() 实际执行 wrapper()

4. 多层装饰器的执行顺序

当多个装饰器堆叠时,从下往上依次包装,但执行时从上往下调用

def decorator1(func):
    def wrapper1():
        print("Decorator 1 before")
        func()
        print("Decorator 1 after")
    return wrapper1

def decorator2(func):
    def wrapper2():
        print("Decorator 2 before")
        func()
        print("Decorator 2 after")
    return wrapper2

@decorator1
@decorator2
def original():
    print("Original function")

original()

输出结果

Decorator 1 before
Decorator 2 before
Original function
Decorator 2 after
Decorator 1 after

原理分析

  1. 装饰顺序:original = decorator1(decorator2(original))
    • 先应用 @decorator2:将 original 包装成 wrapper2
    • 再应用 @decorator1:将 wrapper2 包装成 wrapper1
  2. 调用顺序:执行 wrapper1() → 调用 wrapper2() → 调用 original()

5. 保留函数元信息

多层装饰可能导致函数名(__name__)、文档字符串等元信息丢失。解决方法:

  • 使用 functools.wraps 装饰内部包装函数:
from functools import wraps

def logged(func):
    @wraps(func)  # 将原函数的元信息复制到 wrapper
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

6. 总结关键点

  1. 带参装饰器:工厂函数返回装饰器,装饰器返回包装函数。
  2. 多层装饰器顺序:装饰时从下往上包装,执行时从上往下调用。
  3. 元信息保护:使用 functools.wraps 避免属性丢失。
  4. 调试技巧:可通过打印语句或调试器观察包装链的构建过程。
Python中的装饰器参数传递与多层装饰器执行顺序 1. 问题描述 装饰器是Python中用于修改或增强函数行为的工具。但当装饰器本身需要参数,或多个装饰器同时修饰一个函数时,其执行顺序和参数传递可能令人困惑。例如: 或带参数的装饰器: 面试常问: 装饰器的嵌套顺序如何影响函数行为?带参数的装饰器如何实现? 2. 装饰器基础回顾 装饰器本质是接受函数作为参数的高阶函数。例如: 输出: 3. 带参数的装饰器 若装饰器需要接收外部参数(如 @delay(seconds=2) ),需构造一个 装饰器工厂函数 ,返回真正的装饰器: 执行过程分析 : @repeat(n=3) 先调用 repeat(3) ,返回 actual_decorator ; actual_decorator(say_hello) 返回 wrapper 函数; 调用 say_hello() 实际执行 wrapper() 。 4. 多层装饰器的执行顺序 当多个装饰器堆叠时, 从下往上依次包装 ,但执行时 从上往下调用 : 输出结果 : 原理分析 : 装饰顺序: original = decorator1(decorator2(original)) 先应用 @decorator2 :将 original 包装成 wrapper2 ; 再应用 @decorator1 :将 wrapper2 包装成 wrapper1 。 调用顺序:执行 wrapper1() → 调用 wrapper2() → 调用 original() 。 5. 保留函数元信息 多层装饰可能导致函数名( __name__ )、文档字符串等元信息丢失。解决方法: 使用 functools.wraps 装饰内部包装函数: 6. 总结关键点 带参装饰器 :工厂函数返回装饰器,装饰器返回包装函数。 多层装饰器顺序 :装饰时从下往上包装,执行时从上往下调用。 元信息保护 :使用 functools.wraps 避免属性丢失。 调试技巧 :可通过打印语句或调试器观察包装链的构建过程。