Z
ZHANK
函数与模块

装饰器深入

理解装饰器原理、带参装饰器、类装饰器、functools.wraps

装饰器深入

装饰器是 Python 最强大的特性之一。它不修改原函数,却能为函数增添功能。

学完本章你将掌握: 带参数装饰器、类装饰器、functools.wraps、实战场景。


一、复习:基础装饰器

python
def log(func):
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log
def add(a, b):
    return a + b

print(add(3, 5))  # 调用 add → 8

二、functools.wraps——保留元信息

python
from functools import wraps

def log(func):
    @wraps(func)  # 保留原函数的 __name__、__doc__ 等
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log
def greet():
    """打招呼"""
    print("你好")

print(greet.__name__)  # greet(没有 @wraps 的话是 wrapper)
print(greet.__doc__)   # 打招呼

⚠️ 不加 @wraps 的话,装饰后的函数会丢失原名和文档,调试时很坑。


三、带参数的装饰器

python
from functools import wraps

def repeat(n):
    """重复执行函数 n 次"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello!")

say_hello()  # 打印 3 次

四、类装饰器

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

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

@CountCalls
def greet():
    print("你好")

greet()
greet()
# 第 1 次调用 greet
# 第 2 次调用 greet

五、实战场景

python
# 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:.3f}s")
        return result
    return wrapper

# 2. 缓存装饰器(手动简化版)
def cache(func):
    memo = {}
    @wraps(func)
    def wrapper(*args):
        if args not in memo:
            memo[args] = func(*args)
        return memo[args]
    return wrapper

@cache
def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)

print(fib(35))  # 有缓存,瞬间出结果

六、内置装饰器

python
class MyClass:
    @staticmethod       # 静态方法:不需要 self
    def util(x, y):
        return x + y

    @classmethod        # 类方法:第一个参数是 cls
    def from_string(cls, s):
        return cls(int(s))

    @property           # 属性:像访问属性一样调用方法
    def value(self):
        return self._value * 2

小结

  1. 装饰器 = 包装函数,不修改原函数
  2. 务必用 @functools.wraps 保留元信息
  3. 三层嵌套实现带参数装饰器
  4. @staticmethod @classmethod @property 是内置装饰器