在 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 为元类 passPython 类型模型
第一,所有的 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 = 1Python 真正执行的是下面这段代码:
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__行为,实现自定义逻辑。
执行顺序:
Foo()→ 调用元类的__call__(如果Foo的元类定义了它)。- 在
__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 创建类:Foo2Foo2的元类是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 的方法Foo2是Mymeta的子类,可以访问Mymeta的方法和属性。- 如果
Mymeta不是type的子类(即不是元类),Foo2的元类仍然是type(默认)。
关键区别
| 语法 | 作用 | Foo2.__class__(元类) | Foo2.__bases__(父类) | 适用场景 |
|---|---|---|---|---|
class Foo2(metaclass=Mymeta): | 指定元类 | Mymeta | (object,)(默认) | 动态修改类定义(如 ORM、单例模式) |
class Foo2(Mymeta): | 继承 Mymeta | type(默认) | (Mymeta,) | 普通类继承 |
常见误区
class Mymeta(type):
pass
class Foo2(Mymeta): # ❌ 错误!Mymeta 是元类,不能直接继承
pass- 如果
Mymeta是元类(继承type),Foo2(Mymeta)会报错,因为元类不能直接作为父类。 - 正确做法是
class Foo2(metaclass=Mymeta):。