第79集:类装饰器

学习目标

  • 理解类装饰器的概念
  • 掌握类装饰器的语法
  • 学会创建类装饰器
  • 理解类装饰器的执行流程
  • 掌握类装饰器与函数装饰器的区别
  • 了解类装饰器的应用场景

什么是类装饰器

类装饰器是指使用类来实现的装饰器。类装饰器通过实现__call__方法使类实例可以像函数一样调用,从而实现装饰器的功能。

类装饰器的结构

class MyDecorator:
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

基本语法

语法结构

@MyDecorator
def function():
    pass

等价形式

def function():
    pass

function = MyDecorator(function)

基本示例

示例1:简单类装饰器

class CountCalls:
    """调用计数装饰器"""
    def __init__(self, func):
        self.func = func
        self.count = 0
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"函数 {self.func.__name__} 已调用 {self.count} 次")
        return self.func(*args, **kwargs)

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

say_hello()
say_hello()

示例2:计时装饰器

import time

class Timer:
    """计时装饰器"""
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        start = time.time()
        result = self.func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"{self.func.__name__} 执行时间: {elapsed:.4f}秒")
        return result

@Timer
def slow_function():
    time.sleep(1)
    print("函数执行完成")

slow_function()

示例3:日志装饰器

class Logger:
    """日志装饰器"""
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        print(f"调用函数: {self.func.__name__}")
        print(f"参数: args={args}, kwargs={kwargs}")
        result = self.func(*args, **kwargs)
        print(f"返回值: {result}")
        return result

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

add(3, 5)

类装饰器的执行流程

执行流程详解

class MyDecorator:
    def __init__(self, func):
        print(f"1. __init__ 被调用,函数: {func.__name__}")
        self.func = func
    
    def __call__(self, *args, **kwargs):
        print(f"3. __call__ 被调用")
        result = self.func(*args, **kwargs)
        print(f"5. 函数执行完成")
        return result

@MyDecorator
def my_function():
    print(f"4. 原函数执行")

print("2. 装饰器应用完成")
my_function()

执行顺序

  1. 创建装饰器实例:MyDecorator(my_function)
  2. 调用__init__方法,保存被装饰函数
  3. 将装饰器实例赋值给原函数名
  4. 调用函数时,调用装饰器实例的__call__方法

类装饰器与函数装饰器的区别

区别1:状态管理

# 函数装饰器:需要使用闭包或全局变量
count = 0

def count_calls(func):
    def wrapper(*args, **kwargs):
        global count
        count += 1
        print(f"调用次数: {count}")
        return func(*args, **kwargs)
    return wrapper

# 类装饰器:使用实例属性
class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"调用次数: {self.count}")
        return self.func(*args, **kwargs)

区别2:代码组织

# 函数装饰器:嵌套函数
def decorator(func):
    def wrapper(*args, **kwargs):
        pass
    return wrapper

# 类装饰器:类方法
class Decorator:
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        pass

区别3:扩展性

# 类装饰器更容易扩展
class AdvancedDecorator:
    def __init__(self, func, option1=None, option2=None):
        self.func = func
        self.option1 = option1
        self.option2 = option2
    
    def __call__(self, *args, **kwargs):
        pass
    
    def helper_method(self):
        pass

带参数的类装饰器

示例4:带参数的类装饰器

class Repeat:
    """重复执行装饰器"""
    def __init__(self, times):
        self.times = times
    
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            for i in range(self.times):
                print(f"第{i+1}次执行:")
                func(*args, **kwargs)
        return wrapper

@Repeat(times=3)
def say_hello():
    print("Hello!")

say_hello()

示例5:带参数的日志装饰器

class Log:
    """带参数的日志装饰器"""
    def __init__(self, level="INFO", prefix=""):
        self.level = level
        self.prefix = prefix
    
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(f"[{self.level}] {self.prefix} 调用 {func.__name__}")
            result = func(*args, **kwargs)
            print(f"[{self.level}] {self.prefix} 返回 {result}")
            return result
        return wrapper

@Log(level="DEBUG", prefix="API:")
def add(a, b):
    return a + b

add(3, 5)

类装饰器的应用

应用1:权限验证

class RequirePermission:
    """权限验证装饰器"""
    def __init__(self, permission):
        self.permission = permission
    
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            if not self.has_permission():
                raise PermissionError(f"需要{self.permission}权限")
            return func(*args, **kwargs)
        return wrapper
    
    def has_permission(self):
        return self.permission == "admin"

@RequirePermission("admin")
def delete_user(user_id):
    print(f"删除用户: {user_id}")

delete_user(123)

应用2:缓存装饰器

class Cache:
    """缓存装饰器"""
    def __init__(self, max_size=128):
        self.max_size = max_size
        self.cache = {}
    
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            key = (args, frozenset(kwargs.items()))
            if key in self.cache:
                print("从缓存获取")
                return self.cache[key]
            
            if len(self.cache) >= self.max_size:
                self.cache.popitem()
            
            result = func(*args, **kwargs)
            self.cache[key] = result
            print("计算并缓存")
            return result
        return wrapper

@Cache(max_size=3)
def expensive_function(x):
    print(f"计算 {x}")
    return x ** 2

expensive_function(5)
expensive_function(5)

应用3:重试装饰器

import time

class Retry:
    """重试装饰器"""
    def __init__(self, max_attempts=3, delay=1):
        self.max_attempts = max_attempts
        self.delay = delay
    
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            attempts = 0
            while attempts < self.max_attempts:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    attempts += 1
                    if attempts >= self.max_attempts:
                        raise
                    print(f"重试 {attempts}/{self.max_attempts}: {e}")
                    time.sleep(self.delay)
        return wrapper

@Retry(max_attempts=3, delay=0.5)
def unreliable_function():
    import random
    if random.random() < 0.7:
        raise ValueError("随机错误")
    return "成功"

unreliable_function()

应用4:性能监控

import time

class PerformanceMonitor:
    """性能监控装饰器"""
    def __init__(self, threshold=1.0):
        self.threshold = threshold
    
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            start = time.time()
            result = func(*args, **kwargs)
            elapsed = time.time() - start
            
            if elapsed > self.threshold:
                print(f"警告: {func.__name__} 执行时间 {elapsed:.2f}秒 超过阈值")
            else:
                print(f"{func.__name__} 执行时间 {elapsed:.4f}秒")
            
            return result
        return wrapper

@PerformanceMonitor(threshold=0.5)
def fast_function():
    time.sleep(0.1)

@PerformanceMonitor(threshold=0.5)
def slow_function():
    time.sleep(1.0)

fast_function()
slow_function()

类装饰器的特殊方法

示例6:使用__get__方法

class MethodDecorator:
    """方法装饰器"""
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        print(f"调用方法: {self.func.__name__}")
        return self.func(*args, **kwargs)
    
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return lambda *args, **kwargs: self(instance, *args, **kwargs)

class MyClass:
    @MethodDecorator
    def my_method(self):
        print("方法执行")

obj = MyClass()
obj.my_method()

示例7:使用__getattribute__方法

class AccessLogger:
    """访问日志装饰器"""
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        print(f"访问函数: {self.func.__name__}")
        return self.func(*args, **kwargs)

@AccessLogger
def my_function():
    return "结果"

my_function()

类装饰器的组合

示例8:多个类装饰器

class Timer:
    """计时装饰器"""
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        import time
        start = time.time()
        result = self.func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"执行时间: {elapsed:.4f}秒")
        return result

class Logger:
    """日志装饰器"""
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        print(f"调用函数: {self.func.__name__}")
        result = self.func(*args, **kwargs)
        print(f"返回值: {result}")
        return result

@Logger
@Timer
def add(a, b):
    return a + b

add(3, 5)

示例9:类装饰器与函数装饰器混合

class Timer:
    """计时装饰器"""
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        import time
        start = time.time()
        result = self.func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"执行时间: {elapsed:.4f}秒")
        return result

def log(func):
    """日志装饰器"""
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}")
        result = func(*args, **kwargs)
        print(f"返回值: {result}")
        return result
    return wrapper

@log
@Timer
def multiply(a, b):
    return a * b

multiply(4, 6)

类装饰器的限制

限制1:不能直接装饰类方法

class Decorator:
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

class MyClass:
    @Decorator
    def my_method(self):
        pass

# 需要使用__get__方法
class MethodDecorator:
    def __init__(self, func):
        self.func = func
    
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return lambda *args, **kwargs: self(instance, *args, **kwargs)

限制2:不能直接装饰类

# 类装饰器只能装饰函数,不能装饰类
@Decorator
class MyClass:
    pass

# 需要使用函数装饰器来装饰类
def class_decorator(cls):
    return cls

@class_decorator
class MyClass:
    pass

最佳实践

实践1:使用functools.wraps

from functools import wraps

class MyDecorator:
    def __init__(self, func):
        self.func = func
        wraps(func)(self)
    
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

实践2:清晰的命名

class CountCallsDecorator:
    """清晰的命名"""
    def __init__(self, func):
        self.func = func
        self.call_count = 0
    
    def __call__(self, *args, **kwargs):
        self.call_count += 1
        return self.func(*args, **kwargs)

实践3:文档化装饰器

class TimerDecorator:
    """
    计时装饰器
    
    用于测量函数执行时间
    """
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        import time
        start = time.time()
        result = self.func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"执行时间: {elapsed:.4f}秒")
        return result

实践4:提供辅助方法

class AdvancedDecorator:
    def __init__(self, func):
        self.func = func
        self.call_count = 0
    
    def __call__(self, *args, **kwargs):
        self.call_count += 1
        return self.func(*args, **kwargs)
    
    def get_call_count(self):
        """获取调用次数"""
        return self.call_count
    
    def reset_call_count(self):
        """重置调用次数"""
        self.call_count = 0

常见错误

错误1:忘记实现__call__方法

# 错误:忘记实现__call__方法
class BadDecorator:
    def __init__(self, func):
        self.func = func

# 正确:实现__call__方法
class GoodDecorator:
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

错误2:忘记保存原函数

# 错误:忘记保存原函数
class BadDecorator:
    def __init__(self, func):
        pass
    
    def __call__(self, *args, **kwargs):
        pass

# 正确:保存原函数
class GoodDecorator:
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

错误3:忘记返回结果

# 错误:忘记返回结果
class BadDecorator:
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        self.func(*args, **kwargs)

# 正确:返回结果
class GoodDecorator:
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

总结

类装饰器是Python中强大的工具,具有以下特点:

核心概念

  • 类装饰器通过实现__call__方法实现
  • 类装饰器使用实例属性管理状态
  • 类装饰器可以更好地组织代码

优势

  • 状态管理更方便
  • 代码组织更清晰
  • 扩展性更强
  • 可以添加辅助方法

应用场景

  • 调用计数
  • 性能监控
  • 权限验证
  • 缓存控制
  • 重试机制

掌握类装饰器将帮助你编写更灵活、更强大的Python代码。类装饰器是Python中重要的概念,理解它的工作原理对于编写高质量的Python程序至关重要。

« 上一篇 带参数装饰器 下一篇 » 上下文管理器