在 Python 中,元类(metaclass)是“类的类”。普通类用于创建对象,而元类则用于创建类。换句话说,当你创建一个类时,实际上是在实例化一个元类。默认情况下,Python 中所有类都是 type 的实例,也就是说,type 是默认的元类。

元类允许你在类创建时修改类的行为或结构,例如添加方法、属性,改变类名或基类等。它们对于框架开发和高级编程技巧非常有用,但在日常编程中并不常见。

什么是元类呢?就是定义 Class 的时候,继承 type。元类是类的模板,用于定义类本身的创建方式。所有类(包括 object)的默认元类是 type,除非自己自定义类时,通过 metaclass 指定了元类。

class Mymeta(type):
    def __new__(cls, name, bases, attrs):
        print(f"使用 Mymeta 创建类:{name}")
        return super().__new__(cls, name, bases, attrs)
        
class Foo2(metaclass=Mymeta): # 指定 Mymeta 为元类 pass

Python 类型模型

第一,所有的 Python 的用户定义类,都是 type 这个类的实例。

可能会让你惊讶,事实上,类本身不过是一个名为 type 类的实例。在 Python 的类型世界里,type 这个类就是造物的上帝。

class MyClass:
  pass
 
instance = MyClass()
 
type(instance)
<class '__main__.C'>
type(MyClass)
<class 'type'>

第二,用户自定义类,只不过是 type 类的 __call__ 运算符重载。

当我们定义一个类的语句结束时,真正发生的情况,是 Python 调用 type 的 __call__ 运算符。简单来说,当你定义一个类时,写成下面这样时:

class MyClass:
	data = 1

Python 真正执行的是下面这段代码:

class = type(classname, superclasses, attributedict)

这里等号右边的 type(classname, superclasses, attributedict),就是 type__call__ 运算符重载,它会进一步调用:

type.__new__(typeclass, classname, superclasses, attributedict)
 
type.__init__(class, classname, superclasses, attributedict)

当然,这一切都可以通过代码验证,比如下面这段代码示例:

class MyClass:
  data = 1
  
instance = MyClass()
MyClass, instance
(__main__.MyClass, <__main__.MyClass instance at 0x7fe4f0b00ab8>)
instance.data
1
 
MyClass = type('MyClass', (), {'data': 1})
instance = MyClass()
MyClass, instance
(__main__.MyClass, <__main__.MyClass at 0x7fe4f0aea5d0>)
instance.data
1

由此可见,正常的 MyClass 定义,和你手工去调用 type 运算符的结果是完全一样的。

第三,metaclass 是 type 的子类,通过替换 type 的 __call__ 运算符重载机制,“超越变形”正常的类。

其实,理解了以上几点,我们就会明白,正是 Python 的类创建机制,给了 metaclass 大展身手的机会。

一旦你把一个类型 MyClass 的 metaclass 设置成 MyMeta,MyClass 就不再由原生的 type 创建,而是会调用 MyMeta 的 __call__ 运算符重载。

class = type(classname, superclasses, attributedict) 
# 变为了
class = MyMeta(classname, superclasses, attributedict)

参考链接

18 | metaclass,是潘多拉魔盒还是阿拉丁神灯? - 极客时间已完结课程限时免费阅读

三种魔法方法解读

  • 普通类的 __new____init__ 控制实例的创建和初始化。
  • 元类的 __new____init__ 控制类的创建和初始化。
  • __call__ 方法用于控制类的实例化过程(即当类被调用创建实例时)。它允许你在实例创建前后执行自定义逻辑。

__new__

  • 负责创建类对象(class 本身)。
  • 可以修改类的属性(如方法、类变量等)。
  • 必须返回一个类对象(通常是 type.__new__ 创建的类)。
def __new__(cls, name, bases, namespace, kwargs):
  • cls:元类自身(如 Mymeta)。
  • name:要创建的类的名字(如 "Foo")。
  • bases:父类(如 (object,))。
  • namespace:类的命名空间(包含类属性、方法等)。
  • kwargs:额外的关键字参数(Python 3.6+ 支持)。
class Mymeta(type):
    def __new__(cls, name, bases, namespace):
        print(f"创建类:{name}")
        namespace["version"] = 1.0  # 动态添加类属性
        return super().__new__(cls, name, bases, namespace)
 
class Foo(metaclass=Mymeta):
    pass
 

__init__

  • __new__ 创建类后,初始化类(如验证属性、注册类等)。
  • 不能返回任何值(只用于初始化)。
def __init__(self, name, bases, namespace, kwargs):
  • self:已经创建的类(如 Foo)。
  • 其余参数与 __new__ 相同。
class Mymeta(type):
    def __init__(self, name, bases, namespace):
        print(f"初始化类:{name}")
        if "version" not in namespace:
            raise TypeError("必须定义 version 属性")
 
class Foo(metaclass=Mymeta):
    version = 1.0
 
Foo()

__call__

  • 当类被调用(如 Foo())时,__call__ 会被触发,控制实例的创建和初始化。
  • 可以拦截默认的 __new____init__ 行为,实现自定义逻辑。

执行顺序:

  1. Foo() → 调用元类的 __call__(如果 Foo 的元类定义了它)。
  2. __call__ 内部,通常调用:
    • type.__call__(默认行为)或
    • 手动调用 Foo.__new__Foo.__init__(自定义行为)。
def __call__(cls, *args, kwargs):
  • cls:要实例化的类(如 Foo)。
  • *args, kwargs:传给 Foo() 的参数(如 Foo(1, name="Alice"))。

如果元类没有定义 __call__,Python 会使用 type.__call__,其逻辑如下:

def __call__(cls, *args, kwargs):
    # 1. 调用 cls.__new__ 创建实例
    obj = cls.__new__(cls, *args, kwargs)
    # 2. 如果返回的是 cls 的实例,调用 __init__
    if isinstance(obj, cls):
        obj.__init__(*args, kwargs)
    return obj

你可以重写 __call__ 来拦截实例化过程:

class Mymeta(type):
    def __call__(cls, *args, kwargs):
        print("拦截实例化过程!")
        # 手动控制实例创建和初始化
        obj = cls.__new__(cls, *args, kwargs)
        if isinstance(obj, cls):
            obj.__init__(*args, kwargs)
        return obj
 
class Foo(metaclass=Mymeta):
    def __init__(self, x):
        self.x = x
 
foo = Foo(10)  # 输出:拦截实例化过程!
print(foo.x)   # 输出:10

三者的关系

方法作用对象调用时机典型用途
__new__ (元类)类定义时(class Foo动态修改类属性
__init__ (元类)类定义后验证类结构
__call__ (元类)实例类被调用时(Foo()控制实例化过程

完整执行流程示例

class Mymeta(type):
    def __new__(cls, name, bases, attrs):
        print(f"元类 __new__: 创建类 {name}")
        return super().__new__(cls, name, bases, attrs)
 
    def __init__(self, name, bases, attrs):
        print(f"元类 __init__: 初始化类 {name}")
        super().__init__(name, bases, attrs)
 
    def __call__(cls, *args, kwargs):
        print(f"元类 __call__: 创建 {cls.__name__} 的实例")
        instance = super().__call__(*args, kwargs)
        print("实例创建完成")
        return instance
 
class Foo(metaclass=Mymeta):
    def __new__(cls, x):
        print("Foo __new__")
        return super().__new__(cls)
 
    def __init__(self, x):
        print("Foo __init__")
        self.x = x
 
print("--- 类定义完成 ---")
f = Foo(10)

输出:

元类 __new__: 创建类 Foo
元类 __init__: 初始化类 Foo
--- 类定义完成 ---
元类 __call__: 创建 Foo 的实例
Foo __new__
Foo __init__
实例创建完成

Another Case:

class Mymeta(type):
    def __init__(self, name, bases, dic):
        super().__init__(name, bases, dic)
        print('===>Mymeta.__init__')
        print("From meta class, self.name is:", self.__name__)
        print("From meta class, dic is:", dic)
        print("From meta class, self.yaml_tag is:", self.yaml_tag)
 
    def __new__(cls, *args, kwargs):
        print('===>Mymeta.__new__')
        print("From meta class, cls.name is:",cls.__name__)
        return type.__new__(cls, *args, kwargs)
 
    def __call__(cls, *args, kwargs):
        print('===>Mymeta.__call__')
        obj = cls.__new__(cls)
        cls.__init__(cls, *args, kwargs)
        return obj
 
print('===>start to create Foo class')
class Foo(metaclass=Mymeta):
    yaml_tag = '!Foo'
 
    def __init__(self, name):
        print('Foo.__init__')
        self.name = name
 
    def __new__(cls, *args, kwargs):
        print('Foo.__new__')
        return object.__new__(cls)
 
print("===>start to create Foo2 class")
class Foo2(metaclass=Mymeta):
    yaml_tag = '!Foo2'
 
    def __init__(self, name):
        print('Foo2.__init__')
        self.name = name
 
    def __new__(cls, *args, kwargs):
        print('Foo2.__new__')
        return object.__new__(cls)
 
 
print("===>start to create foo object")
foo = Foo('foo')

输出结果:

===>start to create Foo class
===>Mymeta.__new__
From meta class, cls.name is: Mymeta
===>Mymeta.__init__
From meta class, self.name is: Foo
From meta class, dic is: {'__module__': '__main__', '__qualname__': 'Foo', 'yaml_tag': '!Foo', '__init__': <function Foo.__init__ at 0x000002271FBBAE50>, '__new__': <function Foo.__new__ at 0x000002271FBBA550>}
From meta class, self.yaml_tag is: !Foo
===>start to create Foo2 class
===>Mymeta.__new__
From meta class, cls.name is: Mymeta
===>Mymeta.__init__
From meta class, self.name is: Foo2
From meta class, dic is: {'__module__': '__main__', '__qualname__': 'Foo2', 'yaml_tag': '!Foo2', '__init__': <function Foo2.__init__ at 0x000002271FBF48B0>, '__new__': <function Foo2.__new__ at 0x000002271FBF49D0>}
From meta class, self.yaml_tag is: !Foo2
===>start to create foo object
===>Mymeta.__call__
Foo.__new__
Foo.__init__

典型用途

动态修改类

class Mymeta(type):
    def __new__(cls, name, bases, namespace):
        namespace["author"] = "John"  # 自动添加 author 属性
        return super().__new__(cls, name, bases, namespace)
 
class Foo(metaclass=Mymeta):
    pass
 
print(Foo.author)  # 输出:John

强制类必须有某个属性

class Mymeta(type):
    def __init__(self, name, bases, namespace):
        if "required" not in namespace:
            raise TypeError("必须定义 required 属性")
        super().__init__(name, bases, namespace)
 
class Foo(metaclass=Mymeta):
    required = True  # 必须定义,否则报错

单例模式

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 Singleton(metaclass=SingletonMeta):
    pass
 
a = Singleton()
b = Singleton()
print(a is b)  # True(单例)

对象缓存

class CachedMeta(type):
    _cache = {}
    def __call__(cls, key, *args, kwargs):
        if key not in cls._cache:
            cls._cache[key] = super().__call__(key, *args, kwargs)
        return cls._cache[key]
 
class Database(metaclass=CachedMeta):
    def __init__(self, connection_string):
        self.conn = connection_string
 
db1 = Database("mysql://localhost")  # 新建实例
db2 = Database("mysql://localhost")  # 返回缓存实例
print(db1 is db2)  # True

延迟初始化

class LazyMeta(type):
    def __call__(cls, *args, kwargs):
        print("延迟初始化...")
        # 返回一个代理对象,直到首次访问属性时才真正初始化
        class LazyProxy:
            def __init__(self, *args, kwargs):
                self._args = args
                self._kwargs = kwargs
                self._instance = None
 
            def __getattr__(self, name):
                if self._instance is None:
                    self._instance = super().__call__(*self._args, self._kwargs)
                return getattr(self._instance, name)
 
        return LazyProxy(*args, kwargs)
 
class HeavyResource(metaclass=LazyMeta):
    def __init__(self, size):
        print("加载资源...")
        self.data = [0] * size  # 模拟耗时操作
 
resource = HeavyResource(10_000_000)  # 此时不会真正初始化
print(resource.data[0])              # 首次访问时初始化

参数验证

class ValidatedMeta(type):
    def __call__(cls, *args, kwargs):
        if 'name' not in kwargs:
            raise ValueError("必须提供 name 参数")
        return super().__call__(*args, kwargs)
 
class Person(metaclass=ValidatedMeta):
    def __init__(self, name):
        self.name = name
 
# p = Person()          # 报错:必须提供 name 参数
p = Person(name="Alice")  # 正确

使用元类自动注册所有子类

registry = {}
 
class AutoRegister(type):
    def __new__(cls, name, bases, dct):
        new_cls = super().__new__(cls, name, bases, dct)
        registry[name] = new_cls  # 注册新类到 registry 字典中
        return new_cls
 
class Base(metaclass=AutoRegister):
    pass
 
class SubClass1(Base):
    pass
 
class SubClass2(Base):
    pass
 
print(registry)  # 输出 {'Base': <class '__main__.Base'>, 'SubClass1': <class '__main__.SubClass1'>, 'SubClass2': <class '__main__.SubClass2'>}

继承与元编程

在 Python 中,class Foo2(metaclass=Mymeta):class Foo2(Mymeta): 有本质区别,前者是指定元类(metaclass),后者是继承一个类(Mymeta 作为父类)。它们的用途和效果完全不同。

class Foo2(metaclass=Mymeta):

  • 指定 Mymeta 作为 Foo2 的元类(metaclass),控制 Foo2 类的创建行为(如修改类属性、方法等)。
  • 适用于类工厂、动态修改类定义、ORM 框架(如 Django 的 Model)。
class Mymeta(type):
    def __new__(cls, name, bases, attrs):
        print(f"使用 Mymeta 创建类:{name}")
        return super().__new__(cls, name, bases, attrs)
 
class Foo2(metaclass=Mymeta):  # 指定 Mymeta 为元类
    pass
 
# 输出:使用 Mymeta 创建类:Foo2
  • Foo2 的元类是 Mymeta,但 Foo2 的父类仍然是 object(默认)。
  • Mymeta 控制 Foo2 的创建过程(如 __new____init__)。

class Foo2(Mymeta):

  • Foo2 继承 Mymeta(假设 Mymeta 是一个普通类,而不是元类)。
  • 适用于普通的类继承(如 class Child(Parent):)。
class Mymeta:  # 普通类,不是元类(metaclass)
    def method(self):
        print("Mymeta 的方法")
 
class Foo2(Mymeta):  # Foo2 继承 Mymeta
    pass
 
obj = Foo2()
obj.method()  # 输出:Mymeta 的方法
  • Foo2Mymeta 的子类,可以访问 Mymeta 的方法和属性。
  • 如果 Mymeta 不是 type 的子类(即不是元类),Foo2 的元类仍然是 type(默认)。

关键区别

语法作用Foo2.__class__(元类)Foo2.__bases__(父类)适用场景
class Foo2(metaclass=Mymeta):指定元类Mymeta(object,)(默认)动态修改类定义(如 ORM、单例模式)
class Foo2(Mymeta):继承 Mymetatype(默认)(Mymeta,)普通类继承

常见误区

class Mymeta(type):
    pass
 
class Foo2(Mymeta):  # ❌ 错误!Mymeta 是元类,不能直接继承
    pass
  • 如果 Mymeta 是元类(继承 type),Foo2(Mymeta) 会报错,因为元类不能直接作为父类。
  • 正确做法是 class Foo2(metaclass=Mymeta):