首页 / 知识库 / 0基础入门-阅读资料 / 0基础-python入门到精通

第 15 章 元类

15.1 一切皆对象:类也是对象

在 Python 中,类本身也是对象——它是 type 的实例:

class Dog:
    pass

# Dog 是一个对象
print(type(Dog))          # <class 'type'>
print(type(int))          # <class 'type'>
print(type(str))          # <class 'type'>
print(type(type))         # <class 'type'> — type 是自身的实例

# 类是 type 的实例,对象是类的实例
dog = Dog()
print(type(dog))          # <class 'Dog'>
print(type(Dog))          # <class 'type'>

# 继承关系 vs 实例关系
print(isinstance(dog, Dog))     # True  — dog 是 Dog 的实例
print(isinstance(Dog, type))    # True  — Dog 是 type 的实例
print(issubclass(Dog, object))  # True  — Dog 继承自 object
print(issubclass(type, object)) # True  — type 也继承自 object

15.2 type() 动态创建类

type 有两种用法:

  • type(obj) — 返回对象的类型
  • type(name, bases, dict) — 动态创建类
# 等价于 class Dog(Animal): ...
def bark(self):
    return f"{self.name} says Woof!"

Dog = type(
    "Dog",            # 类名
    (object,),        # 基类元组
    {                 # 类属性字典
        "species": "Canis familiaris",
        "__init__": lambda self, name: setattr(self, "name", name),
        "bark": bark,
    }
)

dog = Dog("Buddy")
print(dog.bark())        # Buddy says Woof!
print(type(dog))         # <class '__main__.Dog'>
# 动态创建类的实际用途——根据配置生成类
def make_model(name, fields):
    def __init__(self, **kwargs):
        for field in fields:
            setattr(self, field, kwargs.get(field))
    
    def __repr__(self):
        attrs = ", ".join(f"{f}={getattr(self, f)!r}" for f in fields)
        return f"{name}({attrs})"
    
    return type(name, (object,), {
        "__init__": __init__,
        "__repr__": __repr__,
        "_fields": fields,
    })

User = make_model("User", ["name", "age", "email"])
user = User(name="Alice", age=30, email="alice@example.com")
print(user)  # User(name='Alice', age=30, email='alice@example.com')

15.3 metaclass 参数

metaclass 指定创建类时使用的元类(默认是 type):

class Meta(type):
    pass

class MyClass(metaclass=Meta):
    pass

print(type(MyClass))  # <class 'Meta'>

15.4 元类的 __new____init__

class Meta(type):
    def __new__(mcs, name, bases, namespace):
        """创建类对象之前调用"""
        print(f"Meta.__new__: 创建类 {name}")
        # 可以修改 name、bases、namespace
        cls = super().__new__(mcs, name, bases, namespace)
        return cls
    
    def __init__(cls, name, bases, namespace):
        """类对象创建之后调用"""
        print(f"Meta.__init__: 初始化类 {name}")
        super().__init__(name, bases, namespace)
    
    def __call__(cls, *args, **kwargs):
        """类被调用时(创建实例时)触发"""
        print(f"Meta.__call__: {cls.__name__} 被调用")
        instance = super().__call__(*args, **kwargs)
        return instance

class MyClass(metaclass=Meta):
    def __init__(self, x):
        self.x = x

# 输出(定义类时):
# Meta.__new__: 创建类 MyClass
# Meta.__init__: 初始化类 MyClass

obj = MyClass(42)
# 输出(创建实例时):
# Meta.__call__: MyClass 被调用

15.5 __prepare__ 控制类命名空间

__prepare__ 返回用于类体执行的命名空间字典:

from collections import OrderedDict

class OrderedMeta(type):
    @classmethod
    def __prepare__(mcs, name, bases):
        return OrderedDict()  # 类属性保持定义顺序
    
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, dict(namespace))
        cls._field_order = list(namespace.keys())
        return cls

class Record(metaclass=OrderedMeta):
    name = "str"
    age = "int"
    email = "str"

print(Record._field_order)
# ['__module__', '__qualname__', 'name', 'age', 'email']
# 禁止重复定义的命名空间
class NoDuplicateDict(dict):
    def __setitem__(self, key, value):
        if key in self and key != "__module__" and key != "__qualname__":
            raise TypeError(f"属性 {key!r} 重复定义")
        super().__setitem__(key, value)

class StrictMeta(type):
    @classmethod
    def __prepare__(mcs, name, bases):
        return NoDuplicateDict()
    
    def __new__(mcs, name, bases, namespace):
        return super().__new__(mcs, name, bases, dict(namespace))

# class Bad(metaclass=StrictMeta):
#     x = 1
#     x = 2  # TypeError: 属性 'x' 重复定义

15.6 元类实战

单例模式

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 Database(metaclass=SingletonMeta):
    def __init__(self):
        print("初始化数据库连接")

db1 = Database()  # 初始化数据库连接
db2 = Database()  # 不再初始化
print(db1 is db2) # True

自动注册

class PluginMeta(type):
    registry = {}
    
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        if bases:  # 不注册基类本身
            PluginMeta.registry[name] = cls
        return cls

class Plugin(metaclass=PluginMeta):
    pass

class ImagePlugin(Plugin):
    pass

class VideoPlugin(Plugin):
    pass

print(PluginMeta.registry)
# {'ImagePlugin': <class 'ImagePlugin'>, 'VideoPlugin': <class 'VideoPlugin'>}

接口检查

class InterfaceMeta(type):
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        
        for base in bases:
            required = getattr(base, "_required_methods", [])
            for method_name in required:
                if method_name not in namespace:
                    raise TypeError(
                        f"{name} 必须实现方法 {method_name}"
                    )
        return cls

class Serializable(metaclass=InterfaceMeta):
    _required_methods = ["serialize", "deserialize"]

# class BadImpl(Serializable):  # TypeError: BadImpl 必须实现方法 serialize
#     pass

class GoodImpl(Serializable):
    def serialize(self):
        return "{}"
    def deserialize(self, data):
        pass

简易 ORM

class ModelMeta(type):
    def __new__(mcs, name, bases, namespace):
        fields = {}
        for key, value in namespace.items():
            if isinstance(value, tuple) and len(value) == 2:
                field_type, default = value
                fields[key] = (field_type, default)
        
        namespace["_fields"] = fields
        cls = super().__new__(mcs, name, bases, namespace)
        return cls

class Model(metaclass=ModelMeta):
    def __init__(self, **kwargs):
        for name, (field_type, default) in self._fields.items():
            value = kwargs.get(name, default)
            if not isinstance(value, field_type):
                raise TypeError(f"{name} 应为 {field_type.__name__}")
            setattr(self, name, value)
    
    def __repr__(self):
        attrs = ", ".join(
            f"{k}={getattr(self, k)!r}" for k in self._fields
        )
        return f"{type(self).__name__}({attrs})"

class User(Model):
    name = (str, "")
    age = (int, 0)
    active = (bool, True)

user = User(name="Alice", age=30)
print(user)  # User(name='Alice', age=30, active=True)

15.7 何时使用元类 vs __init_subclass__ vs 类装饰器

三种自定义类创建行为的方式,从简单到复杂:

类装饰器(最简单)

def add_logging(cls):
    for name, method in vars(cls).items():
        if callable(method) and not name.startswith("_"):
            setattr(cls, name, logged(method))
    return cls

@add_logging
class MyService:
    def process(self):
        pass

__init_subclass__(中等复杂度,Python 3.6+)

class Validated:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        for name, value in vars(cls).items():
            if callable(value) and hasattr(value, "_validate_args"):
                pass  # 添加验证逻辑

元类(最强大但最复杂)

class Meta(type):
    def __new__(mcs, name, bases, namespace):
        # 能修改一切
        pass

选择指南

需求推荐方案
修改类属性或添加方法类装饰器
注册子类、验证子类__init_subclass__
控制类命名空间、修改类创建过程元类
控制实例创建元类的 __call__

经验法则:能用类装饰器解决的不要用 __init_subclass__,能用 __init_subclass__ 解决的不要用元类。元类是最后的手段。


本章小结:元类是”类的类”——控制类的创建过程。虽然强大,但在大多数项目中很少直接使用。理解元类主要是为了理解 Python 的对象模型和类创建机制。实际开发中,优先考虑类装饰器和 __init_subclass__