类与对象创建机制(__new__与__init__)

Python进阶编程专题 · 深入理解类与对象的创建和生命周期

专题:Python进阶编程系统学习

关键词:Python, Python, 类, 对象, __new__, __init__, 实例化, type, object, 单例, 创建机制

一、概述

Python是一门纯面向对象的语言,在Python中"一切皆对象"。理解类与对象的创建机制是掌握Python进阶编程的核心基础之一。许多Python开发者对 __init__ 方法耳熟能详,却对更加底层、更加根本的 __new__ 方法知之甚少。实际上,对象的创建并非仅靠 __init__ 完成,它需要 __new____init__ 两者精密配合。

本文将深入剖析Python中类与对象的创建全过程,从 __new____init__ 的本质区别出发,逐步深入到 type 与 object 的关系、实例化完整流程、单例模式实现、元类对创建过程的干预等高级话题。通过大量代码示例和对比分析,帮助你建立起对Python对象模型清晰而深刻的理解。

核心要点: __new__ 负责创建对象(分配内存),__init__ 负责初始化对象(设置属性)。前者在后者之前被调用,前者返回对象实例后传递给后者。

适用读者: 已掌握Python基础语法,希望深入理解面向对象机制的中高级Python开发者。本文涉及Python 3.x版本的相关实现。

二、核心概念:__new__ 与 __init__

2.1 __init__:初识者的老朋友

__init__ 是Python中最广为人知的魔术方法之一。绝大多数Python学习者在接触面向对象编程时,首先学到的就是通过 __init__ 来初始化对象属性。然而,一个常见的误解是将 __init__ 视为"构造函数"。从严格意义上说,__init__ 并不是构造函数,而是初始化方法(initializer)。

class Person: def __init__(self, name, age): # __init__ 在对象已经创建之后被调用 # self 就是已经创建好的对象实例 self.name = name self.age = age print("__init__ 被调用,self的id:", id(self)) p = Person("Alice", 30)

上面的代码运行后输出 __init__ 被调用,self的id: ...。注意此时 self 参数已经指向一块已经分配完毕的内存空间。这正是 __init__ 的本质——它在对象创建之后被调用,用于对对象进行初始化设置。

2.2 __new__:真正的构造函数

如果说 __init__ 是"装修师傅",那么 __new__ 就是"建筑公司"——它负责真正地创建对象、分配内存。__new__ 是一个静态方法(尽管不需要 @staticmethod 装饰器),它接收类作为第一个参数,并返回一个新的实例。

class Person: def __new__(cls, *args, **kwargs): # cls 是当前正在实例化的类 # 必须返回一个实例对象,通常通过 super().__new__(cls) 实现 instance = super().__new__(cls) print("__new__ 被调用,创建实例 id:", id(instance)) return instance def __init__(self, name, age): print("__init__ 被调用,接收到的 self id:", id(self)) self.name = name self.age = age p = Person("Bob", 25) # 输出顺序: # __new__ 被调用,创建实例 id: 123456 # __init__ 被调用,接收到的 self id: 123456

重要差异: __new__ 的 id(cls) 与 __init__ 的 id(self) 指向的是同一个对象,说明 __new__ 创建的对象直接传给了 __init__ 作为 self 参数。

2.3 两者的关键区别

对比维度__new____init__
本质角色构造函数(真正创建对象)初始化方法(设置初始状态)
调用时机最先被调用在 __new__ 返回实例后被调用
第一个参数cls(类对象)self(实例对象)
返回值必须返回一个实例对象(通常是 cls 的实例)必须返回 None
是否是静态方法是(隐式静态方法)否(实例方法)
能否被重写可以,但不常见可以,非常常见
典型用途单例模式、不可变对象子类化、自定义实例创建属性初始化、资源分配

注意: 如果 __new__ 返回的不是 cls 的实例(或者根本不返回),则 __init__ 将不会自动被调用。这是实现某些特殊设计模式的关键技巧。

三、完整实例化流程解析

3.1 当你写下 ClassName() 时发生了什么

当我们执行类似 p = Person("Alice", 30) 的代码时,Python解释器内部经历了一系列精心设计的步骤。理解这些步骤有助于我们在合适的切入点干预对象创建过程。

# 模拟 Python 的实例化过程 def instantiate(cls, *args, **kwargs): # 步骤1: 调用 __new__ 创建实例 instance = cls.__new__(cls, *args, **kwargs) # 步骤2: 检查返回值的类型 if isinstance(instance, cls): # 步骤3: 如果返回的是正确类型的实例,调用 __init__ instance.__init__(*args, **kwargs) # 步骤4: 返回实例 return instance

上述伪代码清晰地展示了Python实例化的核心逻辑。实际CPython的实现更加复杂,但基本原理完全一致。让我们通过一个加入追踪信息的示例来观察完整的调用链。

class TraceMeta(type): def __call__(cls, *args, **kwargs): print("[元类 __call__] 拦截调用: Person(*args, **kwargs)") # 调用 __new__ instance = cls.__new__(cls, *args, **kwargs) print(f"[元类 __call__] __new__ 返回: {instance}") # 类型检查后调用 __init__ if isinstance(instance, cls): instance.__init__(*args, **kwargs) print("[元类 __call__] __init__ 完成") return instance class Person(metaclass=TraceMeta): def __new__(cls, *args, **kwargs): print("[__new__] 创建新实例") instance = super().__new__(cls) print(f"[__new__] 实例 id: {id(instance)}") return instance def __init__(self, name): print(f"[__init__] 初始化 self id: {id(self)}") self.name = name p = Person("Charlie") # 完整输出: # [元类 __call__] 拦截调用: Person(*args, **kwargs) # [__new__] 创建新实例 # [__new__] 实例 id: 140234567891234 # [元类 __call__] __new__ 返回: <__main__.Person object at 0x...> # [__init__] 初始化 self id: 140234567891234 # [元类 __call__] __init__ 完成

完整调用链: 元类的 __call__ → 类的 __new__(创建对象) → 类的 __init__(初始化对象) → 返回实例。

3.2 当 __new__ 返回非本类实例时

这是理解 __new____init__ 关系的关键边界情况。如果 __new__ 返回的不是当前类的实例,则 __init__ 不会被调用。这一特性可用于实现一些高级模式。

class OnlyOne: _instance = None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def __init__(self, value): # 注意:第二次调用时,__init__ 仍然会被执行! self.value = value a = OnlyOne(1) b = OnlyOne(2) print(a is b) # True(同一个对象) print(a.value) # 2(被第二次__init__覆盖了)

陷阱提醒:__new__ 中实现单例时,__init__ 每次都会被调用!如果你不希望每次调用都重新初始化,必须在 __init__ 中增加保护逻辑,或者使用元类方案。具体见第五节。

3.3 另一个边界:__new__ 抛出异常

如果 __new__ 方法抛出异常,则 __init__ 显然不会被调用,因为根本就没有实例对象需要初始化。同时,ClassName() 调用会将异常向上传播。

class Restricted: def __new__(cls, value): if value < 0: raise ValueError("Restricted 不接受负数") return super().__new__(cls) def __init__(self, value): self.value = value # obj = Restricted(-1) # 抛出 ValueError,__init__ 不会被调用 obj = Restricted(42) # 正常工作

四、type 与 object:Python对象模型的基石

4.1 type 和 object 的关系

在Python的对象模型中,typeobject 是两个最特殊的类,它们构成了Python对象系统的基石。理解两者之间的关系,是掌握Python对象创建机制的最高层次理解。

两条核心规则:
1. object 是所有类的基类(一切类都继承自 object)。
2. type 是所有类的类型(一切类都是 type 的实例)。

也就是说:object 是继承链的顶端,type 是类型系统的顶端。而 type 本身继承自 objectobject 本身又是 type 的实例。这种看似"循环引用"的设计,正是Python对象模型优雅之所在。

# type 与 object 的关系验证 print(object.__bases__) # () —— object 没有基类 print(type.__bases__) # (<class 'object'>,) —— type 继承自 object print(isinstance(object, type)) # True —— object 是 type 的实例 print(isinstance(type, object)) # True —— type 也是 object 的实例(继承关系) print(issubclass(type, object)) # True # 创建 type 自身的是 type 自身 print(type(type)) # <class 'type'>

直观理解: 在Python中,"万物皆对象",每个对象都有"类型"和"基类"两个维度。 type 管理"类型"维度,object 管理"继承"维度。这种设计称为"元类模式",type 本身就是一个元类。

4.2 __new__ 在 type 和 object 中的实现

了解 object.__new__type.__new__ 的区别,有助于理解实例对象和类对象的创建差异。

方法作用创建对象
object.__new__(cls)创建 cls 的一个新实例实例对象
type.__new__(cls, name, bases, namespace)创建一个新的类对象类对象
# object.__new__ 用于创建实例 obj = object.__new__(object) print(obj) # <object object at 0x...> # type.__new__ 用于创建类 MyClass = type.__new__(type, 'MyClass', (object,), {'x': 42}) print(MyClass) # <class '__main__.MyClass'> print(MyClass.x) # 42 # 验证继承关系 print(issubclass(MyClass, object)) # True

4.3 类对象与实例对象的内部表示

Python中的每个对象(无论是类还是实例)在底层都有相同的结构:一个"头部"包含指向其类型的指针,以及一个引用计数字段。类对象额外包含指向基类、方法字典等信息的指针。这种统一的设计使得Python的面向对象系统既灵活又一致。

class MyClass: def method(self): pass obj = MyClass() # 实例对象的内部结构相关属性 print(obj.__class__) # <class '__main__.MyClass'> —— 对象的类型 print(obj.__dict__) # {} —— 实例的属性字典 # 类对象的内部结构相关属性 print(MyClass.__bases__) # (<class 'object'>,) —— 基类 print(MyClass.__dict__) # 类的命名空间(包含方法、属性等) print(MyClass.__name__) # 'MyClass' —— 类名 print(MyClass.__mro__) # 方法解析顺序(Method Resolution Order)

五、单例模式实现方案对比

单例模式是最常用的设计模式之一,保证一个类在整个程序生命周期中只有一个实例。在Python中有多种实现方式,每种方案各有优劣。下面我们对各种方案进行系统对比。

5.1 方案一:基于 __new__ 实现

class SingletonByNew: _instance = None _initialized = False def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def __init__(self, value=None): if not getattr(self, '_initialized', False): self.value = value self._initialized = True # 测试 a = SingletonByNew(1) b = SingletonByNew(2) print(a is b) # True print(a.value) # 1(只初始化一次)

这种方案的优点是简单直接,但需要手动添加 _initialized 标志来防止多次初始化,并且无法阻止通过 type(cls)() 等方式创建第二个实例。

5.2 方案二:基于元类实现

class SingletonMeta(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: # type.__call__ 会依次调用 __new__ 和 __init__ 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("数据库连接初始化"只打印一次)

元类方案优势: 在元类的 __call__ 层面拦截,__init__ 只会被执行一次,无需额外的初始化保护标志。对于整个类系统来说更加"干净"。

5.3 方案三:基于模块导入(最Pythonic)

# singletons.py class DatabaseConnection: def __init__(self): self.url = "mysql://localhost:3306/mydb" # 模块级别直接创建实例 db_connection = DatabaseConnection() # 在其他模块中使用: # from singletons import db_connection # db_connection 天然就是单例

Python模块天然是单例的(模块只在第一次导入时执行一次),利用这一特性是实现单例的最简方案,也是很多Python社区成员推荐的方式。

5.4 四种方案对比

方案线程安全简洁性灵活性推荐度
__new__ 方法否(需加锁)一般
元类 Meta否(需加锁)较好
模块导入是(导入是线程安全的)推荐
装饰器否(需加锁)较好

六、元类与自定义创建机制

6.1 什么是元类

元类(metaclass)是"创建类的类"。当我们定义一个类时,Python默认使用 type 作为元类来创建这个类对象。我们可以通过自定义元类来拦截和控制类的创建过程,从而在类被创建时就施加各种约束或增强。

# 自定义元类:在类创建时自动注入类属性 class AutoPropertyMeta(type): def __new__(mcs, name, bases, namespace): # 自动为所有类添加 created_by 属性 namespace['created_by'] = f"AutoPropertyMeta at {__name__}" namespace['class_version'] = "1.0" return super().__new__(mcs, name, bases, namespace) class Service(metaclass=AutoPropertyMeta): def __init__(self, name): self.name = name svc = Service("AuthService") print(svc.created_by) # 类属性,所有实例共享 print(svc.class_version) # "1.0"

6.2 通过元类实现参数验证类

一个实用的元类应用场景:要求子类必须实现某些方法,或者在类创建时进行参数校验。这比在运行时检查更早地发现问题。

class ValidationMeta(type): def __new__(mcs, name, bases, namespace): # 排除基类 PluginBase 自身 if name != 'PluginBase': # 要求所有插件类必须实现 run 和 stop 方法 required = ['run', 'stop'] for method in required: if method not in namespace: raise TypeError( f"类 '{name}' 必须实现 '{method}' 方法" ) return super().__new__(mcs, name, bases, namespace) class PluginBase(metaclass=ValidationMeta): pass # 正确实现 —— 正常创建 class EmailPlugin(PluginBase): def run(self): print("发送邮件中...") def stop(self): print("停止邮件服务") # 缺少方法 —— 在类定义时就会抛出 TypeError # class BrokenPlugin(PluginBase): # def run(self): pass # # 缺少 stop 方法 → 抛出 TypeError

6.3 元类的 __new__ 与类的 __new__ 对比

为了帮助大家理清这两个层面 __new__ 的关系,我们通过一张对照表来说明:

对比维度元类的 __new__类的 __new__
调用时机类定义时(class 语句执行时)实例化时(ClassName() 调用时)
创建对象创建类对象创建实例对象
第一个参数mcs(元类自身)cls(被实例化的类)
其他参数name, bases, namespace传给构造函数的参数
返回值新创建的类对象新创建的实例对象

七、实际应用场景

7.1 不可变对象的子类化

Python的内置不可变类型(如 tuplestrint)在创建时就已经设定了值,之后无法修改。子类化这些类型时,必须在 __new__ 中完成初始化,因为 __init__ 无法修改不可变对象的值。

class PositiveTuple(tuple): def __new__(cls, iterable): # 过滤出正数 filtered = tuple(x for x in iterable if x > 0) # 通过 super().__new__ 创建 tuple 实例 return super().__new__(cls, filtered) # 注意:不需要(也不能)定义 __init__ # tuple 的 __init__ 没有效果,初始化必须在 __new__ 中完成 pt = PositiveTuple([-3, -1, 0, 2, 5, -2]) print(pt) # (2, 5)

记住: 当你子类化 intstrtuple 等不可变类型并需要自定义初始化逻辑时,必须在 __new__ 中完成。__init__ 在这些场景下无能为力。

7.2 缓存/对象池模式

利用 __new__ 可以实现对象池模式:当请求一个对象时,优先从池中返回已存在的对象,避免频繁创建和销毁。

import weakref class Connection: _pool = weakref.WeakValueDictionary() def __new__(cls, host, port): key = (host, port) if key in cls._pool: instance = cls._pool[key] print(f"复用连接: {host}:{port}") return instance instance = super().__new__(cls) instance._host = host instance._port = port cls._pool[key] = instance print(f"创建新连接: {host}:{port}") return instance def __init__(self, host, port): # 防止覆盖已初始化的对象 pass c1 = Connection("localhost", 8080) c2 = Connection("localhost", 8080) print(c1 is c2) # True

7.3 在 ORM 框架中的应用

许多Python ORM框架(如SQLAlchemy、Django ORM)的内部实现深度使用了 __new__ 和元类机制。当定义模型类时,元类负责扫描类属性中的字段定义,构建内部映射表;当实例化模型对象时,__init__ 接受关键字参数并设置对应的字段值。理解 __new____init__ 的配合,有助于深入理解这些框架的设计思路。

# 简化的 ORM 模型基类演示 class Field: def __init__(self, name, column_type): self.name = name self.column_type = column_type class ModelMeta(type): def __new__(mcs, name, bases, namespace): if name == 'Model': return super().__new__(mcs, name, bases, namespace) # 收集 Field 字段 fields = {} for k, v in namespace.items(): if isinstance(v, Field): fields[k] = v namespace['_fields'] = fields # 自动生成表名 namespace['_table'] = name.lower() return super().__new__(mcs, name, bases, namespace) class Model(metaclass=ModelMeta): def __init__(self, **kwargs): for name, value in kwargs.items(): setattr(self, name, value) def save(self): cols = [] vals = [] for field_name, field in self._fields.items(): value = getattr(self, field_name, None) cols.append(field_name) if isinstance(value, str): vals.append(f"'{value}'") else: vals.append(str(value)) sql = f"INSERT INTO {self._table} ({', '.join(cols)}) VALUES ({', '.join(vals)})" print(f"[SQL]: {sql}") class User(Model): name = Field('name', 'VARCHAR(100)') age = Field('age', 'INT') user = User(name="张三", age=28) user.save() # 输出: [SQL]: INSERT INTO user (name, age) VALUES ('张三', 28)

八、注意事项与常见陷阱

8.1 __new__ 必须返回实例

如果不小心在 __new__ 中忘记了返回对象,Python会返回 None,且 __init__ 不会被调用(因为返回值不是 cls 的实例)。

class BuggyClass: def __new__(cls, *args, **kwargs): pass # 没有 return! def __init__(self): print("这个不会执行") result = BuggyClass() print(result) # None

8.2 必须在 __new__ 中正确调用 super().__new__

子类重写 __new__ 时,通常应该调用 super().__new__(cls) 来让父类完成实际的内存分配。除非你有特殊理由完全替代创建逻辑。

class CorrectNew: def __new__(cls, *args, **kwargs): # 正确的做法:委托给父类 instance = super().__new__(cls) # 也可以在 __new__ 中做一些预初始化 instance.created_at = "预初始化属性" return instance

8.3 __init__ 返回非 None 会引发 TypeError

class BadInit: def __init__(self): return 42 # TypeError: __init__() should return None # b = BadInit() # 抛出 TypeError

8.4 多继承下的 MRO 与 __new__

在多继承情况下,__new__ 的调用遵循MRO(方法解析顺序)。理解这一点对于设计复杂类层次结构至关重要。

class A: def __new__(cls, *args, **kwargs): print("A.__new__") return super().__new__(cls) class B: def __new__(cls, *args, **kwargs): print("B.__new__") return super().__new__(cls) class C(A, B): def __new__(cls, *args, **kwargs): print("C.__new__") return super().__new__(cls) print(C.__mro__) # (<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>) c = C() # 输出顺序: # C.__new__ # A.__new__ # B.__new__

九、总结

核心知识回顾:
1. __new__ 是真正的"构造函数",负责创建对象、分配内存;__init__ 是初始化方法,负责设置对象的初始状态。
2. 完整调用链:元类 __call____new__(创建实例) → __init__(初始化实例) → 返回实例。
3. 如果 __new__ 返回的不是当前类的实例,__init__ 不会被调用。
4. type 是所有类的类型,object 是所有类的基类,二者构成Python对象模型的两大支柱。
5. 单例模式有多种实现方案,模块导入方案最为简洁且线程安全。
6. 元类在类创建层面提供干预能力,可用于验证、注入属性、实现ORM等场景。
7. 子类化不可变类型时,必须在 __new__ 中完成初始化。

理解 __new____init__ 的区别和协作关系,是深入掌握Python对象模型的关键一步。这不仅是理论知识的积累,更能在实际编码中帮助我们编写出更加灵活、可扩展的代码。建议读者在本地环境中运行本文中的代码示例,观察输出结果,加深对Python对象创建机制的理解。

延伸学习: 建议继续学习以下相关主题:
- Python描述符协议(__get____set____delete__
- 上下文管理器(__enter____exit__
- 抽象基类(ABC)与虚拟子类
- Python数据模型完整参考手册