Vanson's Eternal Blog

Python中元类编程详解

Python meta class.png
Published on
/12 mins read/---

Python元类编程

是什么

元类是类的类,即元类是创建类的类。在Python中,默认的元类是type。

元类(Metaclass)是 Python 中一个高级特性,它允许你控制类的创建过程。

作用

元类可以用来控制类的创建过程,比如修改类的属性、方法,或者添加新的功能。

简单案例:

"""
元类Meta在类MyClass被创建时,动态地给它添加了一个greet方法
"""
class Meta(type):
    def __new__(cls, name, bases, dct):
        dct["greet"] = lambda self: f"Hello from {name}"
        return super().__new__(cls, name, bases, dct)
 
class MyClass(metaclass=Meta):
    pass
 
obj = MyClass()
print(obj.greet())  # 输出:Hello from MyClass
 
 

工作过程

  • ‌类创建阶段‌:通过__prepare__方法创建命名空间字典
  • ‌类初始化阶段‌:__new__方法生成类对象,__init__进行属性绑定
  • ‌实例化阶段‌:__call__方法控制实例创建过程‌
 
from collections import OrderedDict
 
"""
‌类定义阶段‌:
__prepare__:创建有序字典存储类属性
__new__:构造类对象,修改命名空间(自动添加__doc__和__slots__)
__init__:初始化类对象,此时类属性已固定
 
‌实例化阶段‌:
__call__:接管实例创建过程
调用用户定义的__new__和__init__
添加额外实例属性_creation_time
 
‌扩展控制点‌:
在元类中可拦截/修改类属性定义(如Django ORM字段收集)
控制实例化时的额外处理(如单例模式、对象池等)
通过__prepare__保留属性顺序(对API文档生成很重要)
 
"""
class MetaLogger(type):
    # -------------------- Phase 1: Class Preparation --------------------
    @classmethod
    def __prepare__(cls, name, bases, **kwargs):
        """Create and return a namespace dictionary with order preservation"""
        print(f"Step-1: [__prepare__] Creating namespace | Class: {name} | Bases: {bases}")
        return OrderedDict()  # Preserve attribute declaration order
 
    # -------------------- Phase 2: Class Construction --------------------
    def __new__(cls, name, bases, namespace, **kwargs):
        """Construct and return the class object"""
        print(f"\nStep-2: [__new__] Building class | Namespace keys: {list(namespace.keys())}")
 
        # Auto-generate docstring if missing
        if '__doc__' not in namespace:
            namespace['__doc__'] = f"Auto-generated class {name}"
 
        # Enforce __slots__ definition
        # if '__slots__' not in namespace:
        #     namespace['__slots__'] = ()
        #     print("[__new__] Warning: __slots__ not defined, added empty tuple")
 
        # Create class using modified namespace
        new_class = super().__new__(cls, name, bases, dict(namespace))
 
        # Add custom class-level metadata
        new_class._meta_author = "MetaLogger"
        return new_class
 
    def __init__(cls, name, bases, namespace, **kwargs):
        """Initialize the class object"""
        super().__init__(name, bases, namespace)
        print(f"Step-3: [__init__] Initialized class | Final attributes: {dir(cls)[:4]}...\n")
 
    # -------------------- Phase 3: Instance Creation --------------------
    def __call__(cls, *args, **kwargs):
        """Control instance instantiation process"""
        print(f"Step-4: [__call__] Creating instance | Args: {args}, Kwargs: {kwargs}")
 
        # Create instance through normal inheritance chain
        instance = super().__call__(*args, **kwargs)
 
        # Attach instance metadata
        instance._created_at = "2023-10-01"
        print("Step-7: [__call__] Instance creation complete")
        return instance
 
 
class DataModel(metaclass=MetaLogger):
    """Custom data model class"""
    version = 1.0
 
    def __new__(cls, value):
        print("\nStep-5: [DataModel __new__] Allocating memory")
        return super().__new__(cls)
 
    def __init__(self, value):
        print("Step-6: [DataModel __init__] Initializing instance")
        self.value = value
 
 
if __name__ == "__main__":
    print("\n========= Class Definition Phase =========")
 
    print("\n========= Instantiation Phase =========")
    obj = DataModel(42)
 
    print("\n========= Final Instance Inspection =========")
    print(f"Step-8: Instance attributes: {vars(obj)}")
    print(f"Step-9: Class metadata: _meta_author={DataModel._meta_author}")
 
 

知识准备

需要提前了解 __slots____new__

Python中的魔术方法

深度使用

实现单例模式

"""
这个元类SingletonMeta确保了SingletonClass只有一个实例。
"""
class SingletonMeta(type):
    _instances = {}
 
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]
 
class SingletonClass(metaclass=SingletonMeta):
    pass
 
obj1 = SingletonClass()
obj2 = SingletonClass()
print(obj1 is obj2)  # 输出:True
 
 

自动注册类

这个元类AutoRegisterMeta会在每个类被创建时,自动将类添加到registry列表中

 
class AutoRegisterMeta(type):
    registry = []
 
    def __new__(cls, name, bases, dct):
        new_cls = super().__new__(cls, name, bases, dct)
        cls.registry.append(new_cls)
        return new_cls
 
class MyClass1(metaclass=AutoRegisterMeta):
    pass
 
class MyClass2(metaclass=AutoRegisterMeta):
    pass
 
print(AutoRegisterMeta.registry)  # 输出:[<class '__main__.MyClass1'>, <class '__main__.MyClass2'>]
 

强制类属性类型

这个元类TypeCheckMeta会在类被创建时,检查属性的类型,并在设置属性值时进行类型检查

 
class TypeCheckMeta(type):
    def __new__(cls, name, bases, dct):
        for attr_name, attr_value in dct.items():
            if isinstance(attr_value, type):
                def type_check(self, value):
                    if not isinstance(value, attr_value):
                        raise TypeError(f"{attr_name} must be of type {attr_value.__name__}")
                    setattr(self, attr_name, value)
                dct[attr_name] = property(lambda self: getattr(self, f"_{attr_name}"), type_check)
        return super().__new__(cls, name, bases, dct)
 
class MyClass(metaclass=TypeCheckMeta):
    attr = int
 
obj = MyClass()
obj.attr = 10
print(obj.attr)  # 输出:10
obj.attr = "not an int"  # 抛出TypeError
 

自定义类的继承逻辑

通过元类可以控制类的继承行为,例如在类被继承时自动添加某些功能或限制继承的条件。

"""
这个元类RestrictiveMeta限制了类的继承,不允许多继承。
"""
class RestrictiveMeta(type):
    def __new__(cls, name, bases, dct):
        if len(bases) > 1:
            raise TypeError("Multiple inheritance is not allowed")
        return super().__new__(cls, name, bases, dct)
 
class Base(metaclass=RestrictiveMeta):
    pass
 
class Child(Base):
    pass
 
class GrandChild(Child, Base):  # 会抛出TypeError
    pass
 
 

实现类的元数据管理

元类可以用来为类添加元数据,这些元数据可以用于描述类的属性、方法、行为等,方便后续的反射操作或框架功能的实现。

"""
这个元类MetaDataMeta为类添加了一个meta属性,用于存储元数据。
"""
class MetaDataMeta(type):
    def __new__(cls, name, bases, dct):
        dct["meta"] = {"description": "This is a class with meta data"}
        return super().__new__(cls, name, bases, dct)
 
class MyClass(metaclass=MetaDataMeta):
    pass
 
print(MyClass.meta)  # 输出:{'description': 'This is a class with meta data'}
 
 

Interface约束模式

"""
强制子类实现指定方法‌
"""
class InterfaceMeta(type):
    def __new__(cls, name, bases, attrs):
        required_methods = {'save', 'delete'}
        for method in required_methods:
            if method not in attrs:
                raise TypeError(f"必须实现 {method} 方法")
        return super().__new__(cls, name, bases, attrs)
 
class Repository(metaclass=InterfaceMeta):
    def save(self): ...
    def delete(self): ...
 
 

框架中的应用

Django ORM

Django的models.Model使用ModelBase元类实现字段注册和数据库映射

# django/db/models/base.py
 
"""
当定义class User(models.Model)时,元类自动将字段存储到_meta.fields,并生成数据库表结构‌
"""
class ModelBase(type):
    def __new__(cls, name, bases, attrs):
        # 收集字段到_meta属性
        new_class = super().__new__(cls, name, bases, attrs)
        if hasattr(new_class, 'Meta'):
            meta_attrs = {}
            for k, v in new_class.Meta.__dict__.items():
                if not k.startswith('__'):
                    meta_attrs[k] = v
            new_class._meta = Options(meta_attrs)
        return new_class
 
 

SQLAlchemy Declarative

通过DeclarativeMeta元类实现ORM映射

# sqlalchemy/ext/declarative/api.py
"""
类属性中的Column对象会被自动收集生成SQL表结构‌
"""
class DeclarativeMeta(type):
    def __init__(cls, classname, bases, dict_):
        # 自动注册Column到__table__
        if '_decl_class_registry' in dict_:
            table_args = []
            for k, v in dict_.items():
                if isinstance(v, Column):
                    setattr(cls, k, v)
                    table_args.append(v)
            cls.__table__ = Table(cls.__tablename__, metadata, *table_args)
        super().__init__(classname, bases, dict_)
 

Pydantic数据验证

在BaseModel元类中实现数据验证逻辑

# pydantic/main.py
"""
该机制使得模型类能自动生成JSON Schema和验证规则‌
"""
class ModelMetaclass(type):
    def __new__(cls, name, bases, namespace):
        # 收集Field类型信息
        fields = {}
        for k, v in namespace.items():
            if isinstance(v, FieldInfo):
                fields[k] = v
        namespace['__fields__'] = fields
        return super().__new__(cls, name, bases, namespace)
 
 

当定义 FastAPI 的请求模型时

"""
元类会自动将 name 和 age 字段存入 __fields__ 属性,用于后续验证‌
"""
from pydantic import BaseModel
 
class UserCreate(BaseModel):
    name: str
    age: int = Field(gt=0)  # 元类会捕获 Field 对象
 
 
 

Fastapi路由系统的元编程

FastAPI 的路由装饰器(如 @app.get)通过 ‌类装饰器模式‌ 实现元编程,动态修改路由行为:

 
# FastAPI 源码中的装饰器核心逻辑
def get(path: str, **kwargs):
    def decorator(func: Callable):
        func.__fastapi_route__ = {
            "path": path,
            "methods": ["GET"]
        }
        return func
    return decorator
 
 

当使用 @app.get("/users") 时,该装饰器会给视图函数添加路由元数据,后续由框架解析生成路由表‌

几点建议

优先使用 __init_subclass__ 替代元类

  • 当只需要在子类创建时执行简单逻辑时,__init_subclass__ 比元类更高效
  • 减少元类继承层级,提高代码可读性
 
# 元类实现方式(较重量级)
class Meta(type):
    def __new__(cls, name, bases, attrs):
        attrs['registry'] = {}  # 类级别注册表
        return super().__new__(cls, name, bases, attrs)
 
class Base(metaclass=Meta):
    pass
 
# 使用 __init_subclass__ 优化(更轻量)
class Base:
    registry = {}
    
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.registry = {}  # 同样实现类级别注册表
        print(f"子类 {cls.__name__} 已注册")
 
class User(Base): pass  # 输出:子类 User 已注册
 
 

利用 __slots__ 减少内存占用

  • __slots__ 会禁用 __dict__,无法动态添加新属性
  • 适合用于需要创建大量实例的类(如ORM模型)
class HeavyClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y
 
# 优化版本(内存减少40-50%)
class OptimizedClass:
    __slots__ = ('x', 'y')  # 显式声明允许的属性
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
 
# 内存对比测试
import sys
print(sys.getsizeof(HeavyClass(1,2)))    # 典型输出:56
print(sys.getsizeof(OptimizedClass(1,2))) # 典型输出:48
 

延迟加载与缓存优化

  • 将元类中的耗时计算(如字段扫描)改为按需执行
  • 使用缓存机制避免重复计算
 
class LazyMeta(type):
    _cache = {}
    
    def __new__(cls, name, bases, attrs):
        # 避免重复计算类属性
        if name not in cls._cache:
            # 模拟耗时操作
            print("正在生成类结构...")
            cls._cache[name] = super().__new__(cls, name, bases, attrs)
        return cls._cache[name]
 
class Product(metaclass=LazyMeta):
    pass
 
# 多次继承测试(仅第一次会执行耗时操作)
class Book(Product): pass  # 输出:正在生成类结构...
class EBook(Product): pass  # 无输出(使用缓存)
 
 

属性访问优化(描述符协议)

  • 将元类中可能涉及的属性访问优化为延迟计算
  • 避免在类创建阶段执行不必要的初始化
 
class CachedProperty:
    """替代 @property 的缓存版本"""
    def __init__(self, func):
        self.func = func
        self.__name__ = func.__name__
    
    def __get__(self, instance, owner):
        if instance is None:
            return self
        # 计算结果并缓存
        res = instance.__dict__[self.__name__] = self.func(instance)
        return res
 
class DataModel:
    @CachedProperty
    def heavy_calculation(self):
        print("执行耗时计算...")
        return sum(i*i for i in range(10**6))
 
d = DataModel()
print(d.heavy_calculation)  # 输出:执行耗时计算... 结果
print(d.heavy_calculation)  # 直接读取缓存(无输出)
 
 

元类与生成器结合(内存敏感场景)

  • 大数据处理框架中的内存优化
  • 避免在元类中一次性加载全部数据
 
class StreamProcessorMeta(type):
    def __new__(cls, name, bases, attrs):
        # 优化数据流处理类的内存使用
        if 'process' in attrs:
            original_method = attrs['process']
            # 将处理方法转换为生成器
            def gen_wrapper(self):
                for item in self.data_stream:
                    yield original_method(self, item)
            attrs['process'] = gen_wrapper
        return super().__new__(cls, name, bases, attrs)
 
class DataStreamer(metaclass=StreamProcessorMeta):
    def __init__(self):
        self.data_stream = range(1000000)
    
    def process(self, item):
        return item * 2
 
ds = DataStreamer()
for result in ds.process():  # 使用生成器逐项处理
    pass  # 内存占用极低