Python 类装饰器
类装饰器通过实现 __call__ 方法使类实例可像函数一样被调用,适合需要维护状态的装饰器场景。
基本结构
Python
from functools import wraps
class CountCalls:
"统计函数调用次数的装饰器"
def __init__(self, func):
self.func = func
self.count = 0
wraps(func)(self) # 保留函数元信息
def __call__(self, *args, **kwargs):
self.count += 1
print(f"Call #{self.count} of {self.func.__name__}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello() # Call #1 of say_hello
say_hello() # Call #2 of say_hello
say_hello() # Call #3 of say_hello
print(f"Total calls: {say_hello.count}") # Total calls: 3
带参数的类装饰器
Python
class Retry:
"重试装饰器:失败后自动重试"
def __init__(self, max_attempts=3, exceptions=(Exception,)):
self.max_attempts = max_attempts
self.exceptions = exceptions
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(1, self.max_attempts + 1):
try:
return func(*args, **kwargs)
except self.exceptions as e:
if attempt == self.max_attempts:
raise
print(f"Attempt {attempt} failed: {e}, retrying...")
return wrapper
@Retry(max_attempts=3, exceptions=(ValueError, ConnectionError))
def fetch_data(url):
import random
if random.random() < 0.7:
raise ValueError("Network error")
return f"Data from {url}"
装饰类
类装饰器也可以用来装饰整个类:
Python
class Singleton:
"单例装饰器"
def __init__(self, cls):
self.cls = cls
self.instance = None
def __call__(self, *args, **kwargs):
if self.instance is None:
self.instance = self.cls(*args, **kwargs)
return self.instance
@Singleton
class Database:
def __init__(self, name):
self.name = name
print(f"Creating database: {name}")
db1 = Database("prod") # Creating database: prod
db2 = Database("dev") # 不会创建新实例
print(db1 is db2) # True
添加属性和方法
类装饰器可以为被装饰对象添加额外功能:
Python
class Memoize:
"缓存装饰器:记忆函数返回值"
def __init__(self, func):
self.func = func
self.cache = {}
wraps(func)(self)
def __call__(self, *args):
if args not in self.cache:
self.cache[args] = self.func(*args)
return self.cache[args]
def clear_cache(self):
"添加缓存清理方法"
self.cache.clear()
print("Cache cleared")
@Memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(100)) # 快速计算
fibonacci.clear_cache() # 清理缓存
与函数装饰器对比
| 特性 | 函数装饰器 | 类装饰器 |
|---|---|---|
| 状态维护 | 需闭包或nonlocal | 直接用实例属性 |
| 复杂度 | 简单场景更简洁 | 复杂逻辑更清晰 |
| 可扩展性 | 添加方法困难 | 可定义多个方法 |
| 可读性 | 嵌套层次多时难懂 | 结构更清晰 |
完整示例:计时装饰器
Python
import time
from functools import wraps
class Timer:
"计时装饰器:记录函数执行时间"
def __init__(self, func=None, *, precision=3):
self.func = func
self.precision = precision
self.total_time = 0
self.call_count = 0
if func:
wraps(func)(self)
def __call__(self, *args, **kwargs):
# 支持 @Timer 和 @Timer(precision=2) 两种用法
if self.func is None:
self.func = args[0]
wraps(self.func)(self)
return self
start = time.perf_counter()
result = self.func(*args, **kwargs)
elapsed = time.perf_counter() - start
self.total_time += elapsed
self.call_count += 1
print(f"{self.func.__name__} executed in {elapsed:.{self.precision}f}s")
return result
def stats(self):
"返回统计信息"
avg = self.total_time / self.call_count if self.call_count else 0
return {
"calls": self.call_count,
"total": round(self.total_time, self.precision),
"average": round(avg, self.precision)
}
@Timer(precision=4)
def slow_function(n):
time.sleep(n)
return n * 2
slow_function(0.1) # slow_function executed in 0.1001s
slow_function(0.2) # slow_function executed in 0.2002s
print(slow_function.stats()) # {'calls': 2, 'total': 0.3003, 'average': 0.1502}
要点总结
| 要点 | 说明 |
|---|---|
| 核心方法 | 实现 __call__ 使实例可调用 |
| 初始化 | __init__ 接收被装饰函数或参数 |
| 状态管理 | 通过实例属性维护装饰器状态 |
| 元信息 | 使用 wraps(func)(self) 保留属性 |
类装饰器适合需要状态管理、多方法扩展的复杂装饰器场景。
D:\git2\jwdev\articles\PYTHON\进阶\装饰器深入\类装饰器.md
📝 发现内容有误?点击此处直接编辑