在元类中,__new__ 和 __init__ 是两个核心方法,它们共同控制类的创建与初始化过程。虽然两者名字相似,但职责和调用时机完全不同。理解它们的区别是掌握元类的关键。
一、元类中 __new__ 与 __init__ 的核心区别
元类的本质是 “创建类的类”,而 __new__ 和 __init__ 分别对应 “类的创建” 和 “类的初始化” 两个阶段:
| 方法 | 作用 | 调用时机 | 返回值 | 核心参数差异 |
|---|---|---|---|---|
__new__ |
创建类对象(类的 “诞生”) | 类定义时,先于 __init__ 调用 |
必须返回一个类对象(通常是新创建的) | 第一个参数是元类本身(cls) |
__init__ |
初始化类对象(类的 “配置”) | 类对象被创建后调用 | 无返回值(None) |
第一个参数是已创建的类对象(cls_obj) |
二、__new__:创建类对象的 “构造器”
__new__ 是元类中第一个被调用的方法,负责实际创建类对象。它的逻辑类似 “工厂”,接收创建类所需的 “原料”(类名、父类、命名空间),并返回一个新的类对象。
1. 方法定义与参数
元类中 __new__ 的标准定义:
class MyMeta(type):
def __new__(cls, name, bases, namespace):
# cls:元类本身(如 MyMeta)
# name:要创建的类的名称(字符串)
# bases:父类组成的元组(继承关系)
# namespace:类的命名空间字典(包含类的属性、方法)
# 逻辑:处理参数,创建并返回类对象
return super().__new__(cls, name, bases, namespace) # 调用父类(type)的__new__
cls:元类自身(例如MyMeta这个类)。name:待创建的类的名称(如定义class User: ...时,name是"User")。bases:待创建的类的父类元组(如class User(Model): ...时,bases是(Model,))。namespace:存储类的属性和方法的字典(如类中定义的__init__、greet等会存入此字典)。
2. 核心作用
- 控制类的创建逻辑:可以在创建类对象前修改
name、bases或namespace(如动态添加属性、修改父类)。 - 决定返回的类对象:
__new__必须返回一个类对象(通常通过super().__new__(...)调用type的__new__方法创建),也可以返回其他类型的对象(但此时__init__不会被调用)。
示例:用 __new__ 动态修改类的属性
class AddPrefixMeta(type):
def __new__(cls, name, bases, namespace):
# 为类的所有方法添加前缀 "prefix_"
new_namespace = {}
for attr_name, attr_value in namespace.items():
if callable(attr_value) and not attr_name.startswith("__"): # 排除特殊方法
new_namespace[f"prefix_{attr_name}"] = attr_value
else:
new_namespace[attr_name] = attr_value
# 调用 type 的 __new__ 创建类对象,传入修改后的命名空间
return super().__new__(cls, name, bases, new_namespace)
# 使用元类
class MyClass(metaclass=AddPrefixMeta):
def greet(self):
return "Hello"
# 类的方法被自动添加前缀
obj = MyClass()
print(obj.prefix_greet()) # 输出:Hello(原 greet 方法被重命名为 prefix_greet)
三、__init__:初始化类对象的 “配置器”
__init__ 在 __new__ 成功创建类对象后调用,负责初始化已创建的类对象(如设置类的额外属性、验证类的结构等)。它不创建类对象,只对 __new__ 返回的类对象进行 “配置”。
1. 方法定义与参数
元类中 __init__ 的标准定义:
class MyMeta(type):
def __init__(cls_obj, name, bases, namespace):
# cls_obj:__new__ 创建的类对象(如 User 类)
# name:类名(同 __new__ 的 name)
# bases:父类元组(同 __new__ 的 bases)
# namespace:类的命名空间(同 __new__ 的 namespace)
# 逻辑:初始化类对象
super().__init__(name, bases, namespace) # 调用父类(type)的__init__
cls_obj:__new__方法返回的类对象(例如User类本身)。- 其他参数(
name、bases、namespace)与__new__相同,但通常用于验证或补充配置。
2. 核心作用
- 初始化类对象的属性:为创建好的类对象添加额外属性或修改现有属性。
- 验证类的合法性:检查类是否符合预设规范(如必须包含某个方法、属性)。
示例:用 __init__ 验证类的结构
class ValidateMeta(type):
def __init__(cls_obj, name, bases, namespace):
# 验证类是否包含 "required_method" 方法
if "required_method" not in namespace:
raise TypeError(f"类 {name} 必须定义 required_method 方法!")
super().__init__(name, bases, namespace)
# 合法类:包含 required_method
class ValidClass(metaclass=ValidateMeta):
def required_method(self):
pass
# 非法类:不包含 required_method → 初始化时抛出异常
try:
class InvalidClass(metaclass=ValidateMeta):
pass
except TypeError as e:
print(e) # 输出:类 InvalidClass 必须定义 required_method 方法!
四、__new__ 与 __init__ 的调用顺序与协作
当定义一个使用自定义元类的类时,执行流程如下:
- 解析类体代码,收集类的属性和方法,生成
namespace字典。 - 调用元类的
__new__方法:传入cls(元类)、name、bases、namespace,创建并返回类对象cls_obj。 - 若
__new__返回的cls_obj是当前元类的实例(即isinstance(cls_obj, MyMeta)),则调用元类的__init__方法:传入cls_obj、name、bases、namespace,初始化类对象。 - 类定义完成,
cls_obj被赋值给类名(如class User(...)中的User)。
关键细节:__new__ 决定 __init__ 是否执行
- 若
__new__返回的是当前元类的实例(通常是通过super().__new__(...)创建的类对象),则__init__会被调用。 - 若
__new__返回的是其他类型的对象(如普通实例、内置类型),则__init__不会被调用。
示例:
class MyMeta(type):
def __new__(cls, name, bases, namespace):
print("调用 __new__")
# 返回一个字符串(非元类实例)
return "not a class"
def __init__(cls_obj, name, bases, namespace):
print("调用 __init__") # 不会执行,因为 __new__ 返回的不是元类实例
# 定义类时,__new__ 返回字符串,__init__ 不执行
class MyClass(metaclass=MyMeta):
pass
print(MyClass) # 输出:not a class(MyClass 被赋值为 __new__ 返回的字符串)
五、对比普通类与元类中的__new__ 和 __init__
为了更好理解,可对比普通类(创建实例)和元类(创建类)中这两个方法的差异:
| 场景 | __new__ 作用 |
__init__ 作用 |
操作对象 |
|---|---|---|---|
| 普通类 | 创建实例对象(instance) |
初始化实例对象(设置 self.xxx) |
实例对象 |
| 元类 | 创建类对象(class) |
初始化类对象(设置 cls.xxx) |
类对象 |
例如,普通类中:
class Person:
def __new__(cls): # 创建实例
print("创建 Person 实例")
return super().__new__(cls)
def __init__(self): # 初始化实例
print("初始化 Person 实例")
p = Person()
# 输出:
# 创建 Person 实例
# 初始化 Person 实例
元类中逻辑类似,但操作对象是类:
class PersonMeta(type):
def __new__(cls, name, bases, namespace): # 创建类
print(f"创建类 {name}")
return super().__new__(cls, name, bases, namespace)
def __init__(cls_obj, name, bases, namespace): # 初始化类
print(f"初始化类 {name}")
class Person(metaclass=PersonMeta):
pass
# 输出:
# 创建类 Person
# 初始化类 Person
总结
元类中的 __new__ 和 __init__ 是类创建过程的两个核心步骤:
__new__负责创建类对象,是类的 “诞生地”,控制类的生成逻辑,必须返回一个类对象(或其他对象)。__init__负责初始化类对象,是类的 “配置中心”,在类对象创建后执行,无返回值。
两者的协作关系是:__new__ 先创建,__init__ 后配置,共同决定了类的最终形态。在自定义元类时,需根据需求选择重写哪个方法:若需修改类的创建逻辑(如动态调整属性、父类),重写 __new__;若需验证或初始化类对象,重写 __init__。