全面 Python 类装饰器与 OOP 模式指南

目录

  1. OOP 核心概念
  2. MVC/MTV 架构模式
  3. 类成员详解
  4. 装饰器完整参考
  5. 实战应用示例
  6. functools.wraps(func)核心作用

OOP 核心概念

四大特性

class Animal:
    # 封装
    def __init__(self, name):
        self._name = name  # 受保护属性

    # 继承
    class Dog(Animal):
        def bark(self):
            print("Woof!")

    # 多态
    def make_sound(self):
        raise NotImplementedError

    # 抽象
    from abc import ABC, abstractmethod
    class Bird(ABC):
        @abstractmethod
        def fly(self):
            pass

类变量 vs 实例变量

class Counter:
    count = 0  # 类变量

    def __init__(self):
        self.id = id(self)  # 实例变量
        Counter.count += 1

MVC/MTV 架构模式

Django 的 MTV 模式

组件 对应传统 MVC 职责
Model Model 数据存取
Template View 展示逻辑
View Controller 业务逻辑

工作流程

  1. 请求 → URL 路由
  2. View 处理请求
  3. Model 操作数据库
  4. Template 渲染响应

类成员详解

三种方法对比

类型 装饰器 首参数 访问权限
实例方法 self 实例+类
类方法 @classmethod cls 仅类
静态方法 @staticmethod

代码示例

class MyClass:
    class_var = "类变量"

    def __init__(self, val):
        self.instance_var = val

    def normal_method(self):
        print(f"实例方法访问 {self.instance_var}")

    @classmethod
    def class_method(cls):
        print(f"类方法访问 {cls.class_var}")

    @staticmethod
    def static_method():
        print("独立静态方法")

装饰器完整参考

核心装饰器

# 属性管理
class Circle:
    @property
    def area(self):
        return 3.14 * self.radius**2

    @area.setter
    def area(self, value):
        self.radius = (value / 3.14)**0.5

# 类构建
@dataclass
class Point:
    x: float
    y: float

# 抽象接口
class Database(ABC):
    @abstractmethod
    def connect(self):
        pass

进阶装饰器

# 缓存优化
from functools import cached_property, lru_cache

class Data:
    @cached_property
    def calculated(self):
        return expensive_computation()

    @lru_cache(maxsize=32)
    def get(self, key):
        return query_database(key)

# 方法重载
from functools import singledispatchmethod

class Processor:
    @singledispatchmethod
    def handle(self, data):
        raise NotImplementedError

    @handle.register(str)
    def _(self, text):
        return f"String: {text}"

实战应用示例

工厂模式

class Vehicle:
    registry = {}

    @classmethod
    def register(cls, name):
        def wrapper(subclass):
            cls.registry[name] = subclass
            return subclass
        return wrapper

    @classmethod
    def create(cls, name, **kwargs):
        return cls.registry[name](**kwargs)

@Vehicle.register("car")
class Car(Vehicle):
    pass

my_car = Vehicle.create("car")

属性验证

class User:
    def __init__(self, email):
        self._email = email

    @property
    def email(self):
        return self._email

    @email.setter
    def email(self, value):
        if not "@" in value:
            raise ValueError("Invalid email")
        self._email = value

最佳实践总结

  1. 封装优先:多用 @property 保护数据
  2. 灵活构造@classmethod 实现多种构造方式
  3. 保持简洁@dataclass 减少样板代码
  4. 明确契约@abstractmethod 定义清晰接口
  5. 性能优化:合理使用缓存装饰器

提示:装饰器可组合使用(如 @classmethod + @property),但需注意执行顺序

实战应用示例

functools.wraps(func)核心作用

@functools.wraps(func) 是装饰器开发的关键工具,用于保留被装饰函数的元信息,解决装饰器导致的元数据丢失问题。

问题演示(不使用wraps)

def bad_decorator(func):
    def wrapper(*args, **kwargs):
        """包装函数的docstring"""
        return func(*args, **kwargs)
    return wrapper

@bad_decorator
def say_hello(name):
    """原始函数的docstring"""
    print(f"Hello, {name}!")

# 元信息测试
print(say_hello.__name__)  # 输出: wrapper(错误!)
print(say_hello.__doc__)   # 输出: 包装函数的docstring(错误!)

导致的问题

  1. 函数名变为装饰器内部函数名(wrapper
  2. 文档字符串被覆盖
  3. 破坏依赖元信息的工具(调试器、文档生成器等)

解决方案(使用wraps)

import functools

def good_decorator(func):
    @functools.wraps(func)  # 关键修复
    def wrapper(*args, **kwargs):
        """包装函数的docstring"""
        return func(*args, **kwargs)
    return wrapper

@good_decorator
def say_hello(name):
    """原始函数的docstring"""
    print(f"Hello, {name}!")

# 元信息测试
print(say_hello.__name__)  # 输出: say_hello(正确)
print(say_hello.__doc__)   # 输出: 原始函数的docstring(正确)

保留的元信息

属性 说明 示例值
__name__ 函数名 "say_hello"
__doc__ 文档字符串 """原始函数的docstring"""
__module__ 所属模块 __main__
__annotations__ 类型注解 {'name': <class 'str'>}
__dict__ 自定义属性 用户定义的属性

实际应用场景

def log_call(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调用: {func.__name__}")  # 显示真实函数名
        return func(*args, **kwargs)
    return wrapper

Web框架路由(Flask示例)

@app.route('/')
@auth_required  # 需要保留元信息才能正确路由
def home():
    return "Welcome"

文档生成

def api_endpoint(func):
    @functools.wraps(func)
    def wrapper():
        return func()
    return wrapper

@api_endpoint
def get_users():
    """获取用户列表"""
    pass

# help(get_users) 能显示真实文档

实现原理

简化版实现

def wraps(original_func):
    def wrapper(decorator_func):
        # 复制所有重要属性
        decorator_func.__name__ = original_func.__name__
        decorator_func.__doc__ = original_func.__doc__
        decorator_func.__module__ = original_func.__module__
        # ...复制其他属性...
        return decorator_func
    return wrapper

实际功能

  1. 将原函数的元数据深拷贝到包装函数
  2. 处理特殊属性(如__qualname__
  3. 兼容各种Python版本

最佳实践

  1. 必须使用的场景:
  2. 任何生产环境装饰器
  3. 会被其他工具(如Sphinx、Flask)使用的函数
  4. 可以不用的场景:
  5. 临时调试装饰器
  6. 明确需要覆盖元信息的情况
  7. 推荐写法
from functools import wraps

def decorator(func):
    @wraps(func)  # 清晰明确的写法
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

扩展知识

保留签名(Python 3.3+)

from inspect import signature

def decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    wrapper.__signature__ = signature(func)  # 保留参数签名
    return wrapper

通过 @functools.wraps + inspect.signature 可以完美保留函数的所有特征