装饰器深入
理解装饰器原理、带参装饰器、类装饰器、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
小结
- 装饰器 = 包装函数,不修改原函数
- 务必用
@functools.wraps保留元信息 - 三层嵌套实现带参数装饰器
@staticmethod@classmethod@property是内置装饰器