一、概述
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的对象模型中,type 和 object 是两个最特殊的类,它们构成了Python对象系统的基石。理解两者之间的关系,是掌握Python对象创建机制的最高层次理解。
两条核心规则:
1. object 是所有类的基类(一切类都继承自 object)。
2. type 是所有类的类型(一切类都是 type 的实例)。
也就是说:object 是继承链的顶端,type 是类型系统的顶端。而 type 本身继承自 object,object 本身又是 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的内置不可变类型(如 tuple、str、int)在创建时就已经设定了值,之后无法修改。子类化这些类型时,必须在 __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)
记住: 当你子类化 int、str、tuple 等不可变类型并需要自定义初始化逻辑时,必须在 __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数据模型完整参考手册