名词(术语)了解--装饰器(decorator)

名词(术语)了解–装饰器(decorator)

在这里插入图片描述

装饰器基础概念

装饰器是Python中的一个重要特性,它允许我们在不修改原有代码的情况下,为函数或类添加新的功能。本质上,装饰器是一个返回函数的函数。

1. 基本装饰器

最简单的装饰器形式:

def simple_decorator(func):
    def wrapper():
        print("开始执行")
        func()
        print("执行结束")
    return wrapper

@simple_decorator
def hello():
    print("Hello, World!")

# 调用函数
hello()
# 输出:
# 开始执行
# Hello, World!
# 执行结束

2. 带参数的装饰器

def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

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

greet("张三")
# 输出三次:你好, 张三!

3. 保留函数元数据的装饰器

from functools import wraps

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

@log_decorator
def add(a, b):
    """计算两个数的和"""
    return a + b

print(add.__name__)  # 输出: add
print(add.__doc__)   # 输出: 计算两个数的和

4. 类装饰器

class Timer:
    def __init__(self, func):
        self.func = func
        
    def __call__(self, *args, **kwargs):
        import time
        start = time.time()
        result = self.func(*args, **kwargs)
        end = time.time()
        print(f"函数 {self.func.__name__} 执行时间: {end - start:.2f} 秒")
        return result

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

slow_function()

5. 实用装饰器示例

5.1 缓存装饰器

def cache_decorator(func):
    cache = {}
    def wrapper(*args):
        if args in cache:
            return cache[args]
        result = func(*args)
        cache[args] = result
        return result
    return wrapper

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

print(fibonacci(10))  # 计算会很快

5.2 错误重试装饰器

import time

def retry(max_attempts=3, delay=1):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise
                    print(f"尝试失败,{delay}秒后重试...")
                    time.sleep(delay)
            return None
        return wrapper
    return decorator

@retry(max_attempts=3, delay=1)
def unstable_function():
    import random
    if random.random() < 0.7:
        raise Exception("随机错误")
    return "成功"

5.3日志装饰器

```python
import logging

def log_function_call(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        logging.info(f"调用函数: {func.__name__}")
        try:
            result = func(*args, **kwargs)
            logging.info(f"函数 {func.__name__} 执行成功")
            return result
        except Exception as e:
            logging.error(f"函数 {func.__name__} 执行失败: {str(e)}")
            raise
    return wrapper

@log_function_call
def divide(a, b):
    return a / b
```

6. 内置装饰器的使用

class Person:
    def __init__(self):
        self._name = ""

    @property
    def name(self):
        """获取姓名"""
        return self._name

    @name.setter
    def name(self, value):
        """设置姓名"""
        self._name = value

    @staticmethod
    def static_method():
        """静态方法"""
        print("这是静态方法")

    @classmethod
    def class_method(cls):
        """类方法"""
        print("这是类方法")

7. 装饰器的使用建议

  1. 单一职责原则:每个装饰器应该只做一件事。

  2. 使用functools.wraps:保留原函数的元数据。

  3. 参数验证:在装饰器中进行参数验证。

  4. 错误处理:适当的异常处理和日志记录。

  5. 文档化:为装饰器添加清晰的文档字符串。

def validate_params(func):
    """参数验证装饰器"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 参数验证逻辑
        if not args and not kwargs:
            raise ValueError("函数参数不能为空")
        return func(*args, **kwargs)
    return wrapper

@validate_params
def process_data(data):
    """处理数据的函数"""
    return data

8. 装饰器的注意事项

  1. 顺序很重要:多个装饰器的执行顺序是从下到上的。
@decorator1
@decorator2
def function():
    pass
# 相当于: decorator1(decorator2(function))
  1. 使用functools.wraps:为了保留原函数的元数据,建议使用@functools.wraps

  2. 类方法装饰器:在类中使用装饰器时要注意self参数:

class MyClass:
    @timer
    def my_method(self, x):
        return x * 2
  1. 装饰器的性能影响:装饰器会增加函数调用的开销,在性能敏感的场景要谨慎使用。

9. 多重装饰器

def bold(func):
    def wrapper():
        return "<b>" + func() + "</b>"
    return wrapper

def italic(func):
    def wrapper():
        return "<i>" + func() + "</i>"
    return wrapper

@bold
@italic
def hello():
    return "Hello, World!"

print(hello())  # 输出: <b><i>Hello, World!</i></b>

装饰器是Python中非常强大的特性,它们可以帮助我们:

  1. 减少代码重复
  2. 提高代码的可读性
  3. 实现横切关注点(如日志、性能监控等)
  4. 动态修改函数或类的行为

装饰器扩展

在这里插入图片描述

1. 描述符协议

描述符是实现了特定协议的类,可以用来自定义属性的访问方式:

class TypedProperty:
    def __init__(self, name, type_):
        self.name = name
        self.type = type_
    
    def __get__(self, instance, cls):
        if instance is None:
            return self
        return instance.__dict__.get(self.name)
    
    def __set__(self, instance, value):
        if not isinstance(value, self.type):
            raise TypeError(f'期望类型 {self.type}')
        instance.__dict__[self.name] = value

class Person:
    name = TypedProperty('name', str)
    age = TypedProperty('age', int)
    
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

# 使用
person = Person("张三", 25)
try:
    person.age = "invalid"  # 将引发TypeError
except TypeError as e:
    print(f"错误: {e}")

2. 异步装饰器

支持异步函数的装饰器:

import asyncio
import functools

def async_retry(retries=3, delay=1):
    def decorator(func):
        @functools.wraps(func)
        async def wrapper(*args, **kwargs):
            for attempt in range(retries):
                try:
                    return await func(*args, **kwargs)
                except Exception as e:
                    if attempt == retries - 1:
                        raise
                    print(f"尝试 {attempt + 1} 失败,等待 {delay} 秒...")
                    await asyncio.sleep(delay)
            return None
        return wrapper
    return decorator

@async_retry(retries=3, delay=1)
async def fetch_data():
    # 模拟异步操作
    import random
    if random.random() < 0.7:
        raise ConnectionError("连接错误")
    return "数据获取成功"

# 使用示例
async def main():
    try:
        result = await fetch_data()
        print(result)
    except ConnectionError as e:
        print(f"最终失败: {e}")

# asyncio.run(main())

3. 上下文装饰器

结合上下文管理器的装饰器:

from contextlib import contextmanager
import time

class Timer:
    def __init__(self, name):
        self.name = name
        
    def __enter__(self):
        self.start = time.time()
        return self
        
    def __exit__(self, *args):
        self.end = time.time()
        self.duration = self.end - self.start
        print(f"{self.name} 耗时: {self.duration:.2f} 秒")

def with_timer(func):
    def wrapper(*args, **kwargs):
        with Timer(func.__name__):
            return func(*args, **kwargs)
    return wrapper

@with_timer
def slow_operation():
    time.sleep(1)
    return "操作完成"

4. 参数验证装饰器

高级参数验证示例:

from typing import get_type_hints
import functools

def validate_types(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # 获取函数的类型注解
        hints = get_type_hints(func)
        
        # 检查位置参数
        for arg, value in zip(func.__code__.co_varnames, args):
            if arg in hints:
                if not isinstance(value, hints[arg]):
                    raise TypeError(
                        f"参数 {arg} 必须是 {hints[arg].__name__} 类型"
                    )
        
        # 检查关键字参数
        for key, value in kwargs.items():
            if key in hints:
                if not isinstance(value, hints[key]):
                    raise TypeError(
                        f"参数 {key} 必须是 {hints[key].__name__} 类型"
                    )
        
        return func(*args, **kwargs)
    return wrapper

@validate_types
def process_user(name: str, age: int, scores: list = None):
    return f"用户 {name}, 年龄 {age}, 分数 {scores}"

# 使用示例
try:
    result = process_user("张三", "25")  # 将引发TypeError
except TypeError as e:
    print(f"错误: {e}")

5. 装饰器工厂

动态创建装饰器的高级模式:

def create_validator(validation_func, error_message):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if not validation_func(*args, **kwargs):
                raise ValueError(error_message)
            return func(*args, **kwargs)
        return wrapper
    return decorator

# 创建具体的验证装饰器
def positive_args(func):
    return create_validator(
        lambda *args, **kwargs: all(arg > 0 for arg in args),
        "所有参数必须为正数"
    )(func)

def non_empty_string(func):
    return create_validator(
        lambda s: isinstance(s, str) and bool(s.strip()),
        "字符串不能为空"
    )(func)

# 使用示例
@positive_args
def calculate_area(width, height):
    return width * height

@non_empty_string
def greet(name):
    return f"Hello, {name}!"

6. 单例模式装饰器

def singleton(cls):
    instances = {}
    
    @functools.wraps(cls)
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    
    return get_instance

@singleton
class Database:
    def __init__(self):
        print("初始化数据库连接...")
        
    def query(self):
        print("执行查询...")

# 使用
db1 = Database()
db2 = Database()
print(db1 is db2)  # 输出: True

7. 属性缓存装饰器

def cached_property(func):
    name = func.__name__

    @property
    @functools.wraps(func)
    def wrapper(self):
        if not hasattr(self, f"_cached_{name}"):
            setattr(self, f"_cached_{name}", func(self))
        return getattr(self, f"_cached_{name}")
    return wrapper

class ExpensiveComputation:
    def __init__(self, data):
        self.data = data
    
    @cached_property
    def processed_data(self):
        print("处理数据...")
        return sum(self.data)

# 使用
comp = ExpensiveComputation([1, 2, 3, 4, 5])
print(comp.processed_data)  # 首次计算
print(comp.processed_data)  # 使用缓存

8. 方法装饰器与类装饰器的结合

def log_methods(cls):
    """记录类中所有方法调用的装饰器"""
    class_dict = cls.__dict__.copy()
    
    for name, method in class_dict.items():
        if callable(method):
            setattr(cls, name, log_method(method))
    
    return cls

def log_method(method):
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        print(f"调用方法 {method.__name__}")
        result = method(self, *args, **kwargs)
        print(f"方法 {method.__name__} 返回 {result}")
        return result
    return wrapper

@log_methods
class Calculator:
    def add(self, x, y):
        return x + y
    
    def multiply(self, x, y):
        return x * y

# 使用
calc = Calculator()
calc.add(2, 3)
calc.multiply(4, 5)

9. 装饰器的错误处理和调试

import sys
import traceback

def debug(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        try:
            print(f"调用 {func.__name__}")
            print(f"参数: {args}, {kwargs}")
            result = func(*args, **kwargs)
            print(f"返回: {result}")
            return result
        except Exception as e:
            print(f"错误: {e}")
            print("调用栈:")
            traceback.print_exc(file=sys.stdout)
            raise
    return wrapper

@debug
def divide(a, b):
    return a / b

这些高级特性和扩展使得装饰器成为Python中非常强大和灵活的工具。它们可以用于:

  1. 代码复用和模块化
  2. 横切关注点的处理
  3. 元编程和代码生成
  4. 性能优化和缓存
  5. 参数验证和类型检查
  6. 设计模式实现

在使用这些高级特性时,需要注意:

  1. 保持代码的可读性和可维护性
  2. 适当的错误处理和异常传播
  3. 性能影响的考虑
  4. 文档的完整性
  5. 测试的覆盖率
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值