4、Python 面试题解析:什么是装饰器(decorator)?

Python 装饰器(Decorator)详解

装饰器是 Python 中一种强大的工具,用于动态修改函数或类的行为,而无需修改其源代码。它基于高阶函数和闭包的概念,广泛应用于日志记录、权限验证、性能测试等场景。


一、装饰器的核心概念
  1. 定义
    装饰器是一个接受函数作为参数并返回新函数的可调用对象(通常是函数或类)。

  2. 作用

    • 在不修改原函数代码的情况下,增强其功能。
    • 实现代码复用,遵循 DRY(Don’t Repeat Yourself)原则。
  3. 语法糖
    Python 提供 @decorator 语法,简化装饰器的使用。


二、装饰器的工作原理
  1. 函数作为对象
    Python 中函数是一等公民,可以作为参数传递、返回值或赋值给变量。

  2. 闭包机制
    装饰器利用闭包(Closure)保存被装饰函数的引用,并在新函数中调用它。

  3. 执行顺序

    • 装饰器在函数定义时立即执行。
    • 返回的新函数替换原函数。

三、装饰器的实现步骤
  1. 定义装饰器函数
    接受一个函数作为参数,返回一个新函数。

  2. 编写新函数
    在新函数中添加额外功能,并调用原函数。

  3. 应用装饰器
    使用 @decorator 语法或手动调用装饰器。


四、代码示例
示例 1:简单装饰器

记录函数的执行时间:

import time

def timer_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} executed in {end_time - start_time:.4f} seconds")
        return result
    return wrapper

@timer_decorator
def heavy_computation(n):
    time.sleep(n)
    return "Done"

# 调用被装饰的函数
print(heavy_computation(2))

输出:

heavy_computation executed in 2.0023 seconds
Done
示例 2:带参数的装饰器

根据日志级别记录信息:

def log_decorator(level="INFO"):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"[{level}] Calling {func.__name__} with args={args}, kwargs={kwargs}")
            result = func(*args, **kwargs)
            print(f"[{level}] {func.__name__} returned {result}")
            return result
        return wrapper
    return decorator

@log_decorator(level="DEBUG")
def add(a, b):
    return a + b

# 调用被装饰的函数
print(add(3, 5))

输出:

[DEBUG] Calling add with args=(3, 5), kwargs={}
[DEBUG] add returned 8
8
示例 3:类装饰器

统计函数调用次数:

class CallCounter:
    def __init__(self, func):
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"{self.func.__name__} has been called {self.count} times")
        return self.func(*args, **kwargs)

@CallCounter
def greet(name):
    return f"Hello, {name}!"

# 调用被装饰的函数
print(greet("Alice"))
print(greet("Bob"))

输出:

greet has been called 1 times
Hello, Alice!
greet has been called 2 times
Hello, Bob!
示例 4:多个装饰器叠加

同时记录日志和执行时间:

@log_decorator(level="INFO")
@timer_decorator
def multiply(a, b):
    return a * b

# 调用被装饰的函数
print(multiply(4, 5))

输出:

[INFO] Calling multiply with args=(4, 5), kwargs={}
multiply executed in 0.0000 seconds
[INFO] multiply returned 20
20

五、装饰器的底层机制
  1. 等价代码解析
    @decorator 语法糖等价于:

    def original_function():
        pass
    original_function = decorator(original_function)
    
  2. 函数属性保留
    使用 functools.wraps 保留原函数的元信息(如 __name____doc__):

    from functools import wraps
    
    def my_decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print("Before calling the function")
            result = func(*args, **kwargs)
            print("After calling the function")
            return result
        return wrapper
    

六、装饰器的应用场景
  1. 日志记录
    自动记录函数调用信息。

  2. 权限验证
    检查用户权限后再执行函数。

  3. 性能测试
    测量函数执行时间。

  4. 缓存结果
    使用 functools.lru_cache 实现函数结果缓存。

  5. 重试机制
    在函数失败时自动重试。


七、装饰器的注意事项
  1. 装饰器顺序
    多个装饰器从上到下依次应用:

    @decorator1
    @decorator2
    def func():
        pass
    # 等价于 func = decorator1(decorator2(func))
    
  2. 装饰器副作用
    装饰器在函数定义时立即执行,而非调用时。

  3. 类方法装饰器
    装饰类方法时,需使用 @functools.wraps 保留方法绑定。


八、总结

装饰器是 Python 中实现 AOP(面向切面编程)的核心工具,通过高阶函数和闭包机制,能够在不修改原代码的情况下增强函数功能。掌握装饰器的使用和原理,可以显著提升代码的可维护性和复用性。

最终示例:综合应用
from functools import wraps

def retry_decorator(max_retries=3):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"Attempt {attempt + 1} failed: {e}")
            raise Exception("All retries failed")
        return wrapper
    return decorator

@retry_decorator(max_retries=2)
def risky_operation():
    import random
    if random.random() < 0.5:
        raise ValueError("Something went wrong")
    return "Success"

# 调用被装饰的函数
print(risky_operation())

输出(示例):

Attempt 1 failed: Something went wrong
Attempt 2 failed: Something went wrong
Traceback (most recent call last):
  ...
Exception: All retries failed

你可能感兴趣的:(python,开发语言)