← 返回首页
🎯

装饰器详解

📂 python ⏱ 2 min 266 words

装饰器详解

装饰器是Python中最强大的语法特性之一,它允许你在不修改原函数代码的情况下,为函数添加额外的功能。装饰器本质上是一个高阶函数,接受一个函数作为参数,返回一个新函数。

基本装饰器

装饰器使用 @ 语法糖,将一个函数应用到另一个函数上:

import functools

def timer(func):
    """计时装饰器"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        import time
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} 执行耗时: {end - start:.4f}秒")
        return result
    return wrapper

@timer
def slow_function():
    import time
    time.sleep(1)
    return "完成"

slow_function()
# 输出: slow_function 执行耗时: 1.0012秒

带参数的装饰器

当装饰器需要接受参数时,需要再套一层函数:

import functools

def repeat(times):
    """重复执行装饰器"""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            result = None
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(times=3)
def greet(name):
    print(f"你好, {name}!")

greet("Python")
# 输出3次: 你好, Python!

functools.wraps 的作用

使用装饰器时,原函数的元信息(如函数名、文档字符串)会被wrapper函数覆盖。functools.wraps 可以保留这些信息:

import functools

def my_decorator(func):
    @functools.wraps(func)  # 保留原函数信息
    def wrapper(*args, **kwargs):
        """wrapper的文档"""
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def example():
    """这是example的文档"""
    pass

print(example.__name__)  # example(而非wrapper)
print(example.__doc__)   # 这是example的文档

类装饰器

除了函数,类也可以作为装饰器。类装饰器需要实现 __call__ 方法:

import functools

class CountCalls:
    """统计函数调用次数的类装饰器"""
    def __init__(self, func):
        functools.update_wrapper(self, func)
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"调用 {self.func.__name__} 第 {self.num_calls} 次")
        return self.func(*args, **kwargs)

    def reset_count(self):
        self.num_calls = 0

@CountCalls
def say_hello():
    print("Hello!")

say_hello()  # 调用 say_hello 第 1 次
say_hello()  # 调用 say_hello 第 2 次
print(say_hello.num_calls)  # 2

装饰器堆叠

多个装饰器可以堆叠使用,执行顺序从下到上:

import functools

def bold(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return f"<b>{func(*args, **kwargs)}</b>"
    return wrapper

def italic(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return f"<i>{func(*args, **kwargs)}</i>"
    return wrapper

@bold
@italic
def hello(name):
    return f"Hello, {name}"

print(hello("World"))  # <b><i>Hello, World</i></b>

实战:缓存装饰器

import functools

def memoize(func):
    """带缓存的装饰器"""
    cache = {}

    @functools.wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    
    wrapper.cache = cache
    return wrapper

@memoize
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(100))  # 秒出结果

常见使用场景

掌握装饰器是Python进阶的关键一步,它让你的代码更加优雅和模块化。