在元类中,__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. 核心作用

  • 控制类的创建逻辑:可以在创建类对象前修改 namebases 或 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 类本身)。
  • 其他参数(namebasesnamespace)与 __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__ 的调用顺序与协作

当定义一个使用自定义元类的类时,执行流程如下:

  1. 解析类体代码,收集类的属性和方法,生成 namespace 字典。
  2. 调用元类的 __new__ 方法:传入 cls(元类)、namebasesnamespace,创建并返回类对象 cls_obj
  3. 若 __new__ 返回的 cls_obj 是当前元类的实例(即 isinstance(cls_obj, MyMeta)),则调用元类的 __init__ 方法:传入 cls_objnamebasesnamespace,初始化类对象。
  4. 类定义完成,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__