Python中__init__与__new__方法的详解与比较
1. __new__
方法
• 作用:负责创建对象实例(实际的内存分配)。
• 调用时机:在对象实例化时最先被调用。
• 特点:
• 是一个静态方法(但不需要用 @staticmethod
显式声明)。
• 必须返回一个实例对象(通常是当前类的实例,但可以是其他类的实例)。
• 如果返回的不是实例,__init__
将不会执行。
• 语法:
def __new__(cls, *args, **kwargs):
return super().__new__(cls) # 调用父类的 __new__ 创建实例
使用场景
• 控制实例的创建逻辑(例如单例模式)。
• 继承不可变类型(如 str
, tuple
)时定制对象创建。
• 动态决定返回哪个类的实例。
示例
class Singleton:
_instance = None
def __new__(cls):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
a = Singleton()
b = Singleton()
print(a is b) # 输出 True(单例)
2. __init__
方法
• 作用:负责初始化实例(设置初始状态)。
• 调用时机:在 __new__
返回实例后自动调用。
• 特点:
• 是一个实例方法,第一个参数是 self
(即 __new__
返回的实例)。
• 不能有返回值(否则会抛出 TypeError
)。
• 语法:
def __init__(self, *args, **kwargs):
self.attribute = value # 初始化对象属性
使用场景
• 设置对象的初始属性。
• 执行对象创建后的初始化逻辑(如打开文件、连接数据库)。
示例
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Alice", 30)
print(p.name) # 输出 "Alice"
3. 两者协作流程
当执行 obj = MyClass(args)
时:
__new__
被调用:MyClass.__new__(cls, args)
创建实例。- 实例传递给
__init__
:如果__new__
返回当前类的实例,__init__
被调用,接收实例作为self
。 - 初始化完成:最终返回初始化后的对象。
4. 关键区别
特性 | __new__ |
__init__ |
---|---|---|
角色 | 创建实例 | 初始化实例 |
调用顺序 | 最先调用 | 在 __new__ 之后调用 |
返回值 | 必须返回实例 | 无返回值(None ) |
参数 | 第一个参数是 cls (类) |
第一个参数是 self (实例) |
是否必需 | 默认由 object 提供 |
可选(不定义则无初始化操作) |
5. 高级用法示例
继承不可变类型
class UpperStr(str):
def __new__(cls, value):
return super().__new__(cls, value.upper())
s = UpperStr("hello")
print(s) # 输出 "HELLO"
动态返回不同实例
class BaseClass:
def __new__(cls, value):
if value > 0:
return Positive()
else:
return Negative()
class Positive:
pass
class Negative:
pass
obj = BaseClass(5)
print(type(obj)) # 输出 <class '__main__.Positive'>
总结
• 优先使用 __init__
:除非你需要控制实例的创建过程,否则无需重写 __new__
。
• 谨慎重写 __new__
:它会影响对象创建的核心逻辑,容易引入隐蔽的 Bug。
• 理解协作关系:__new__
和 __init__
共同完成对象从创建到初始化的完整流程。
下面再提供几个具体的代码示例,帮助你更深入理解 __new__
和 __init__
的不同应用场景:
示例 1:参数预验证(在 __new__
中拦截无效参数)
class PositiveInteger:
def __new__(cls, value):
# 在创建实例前验证参数
if not isinstance(value, int) or value <= 0:
raise ValueError("必须为正整数")
# 调用父类的 __new__ 创建实例
instance = super().__new__(cls)
return instance
def __init__(self, value):
# 此时 value 已经被验证为合法
self.value = value
# 合法
a = PositiveInteger(5)
print(a.value) # 输出 5
# 非法(触发 __new__ 中的异常)
try:
b = PositiveInteger(-3)
except ValueError as e:
print(e) # 输出 "必须为正整数"
关键点:
• __new__
在实例化前拦截非法参数,避免无效对象进入 __init__
流程。
示例 2:不可变对象的属性初始化(直接在 __new__
中设置属性)
class ImmutablePoint:
def __new__(cls, x, y):
instance = super().__new__(cls)
# 直接操作实例的 __dict__ 设置属性(避免触发 __setattr__)
instance.__dict__['x'] = x
instance.__dict__['y'] = y
return instance
def __setattr__(self, name, value):
# 禁止修改属性
raise AttributeError("ImmutablePoint 不可修改")
p = ImmutablePoint(2, 3)
print(p.x, p.y) # 输出 2 3
try:
p.x = 5 # 触发错误
except AttributeError as e:
print(e) # 输出 "ImmutablePoint 不可修改"
关键点:
• 不可变对象通常需要在 __new__
中初始化属性,因为 __init__
中无法绕过 __setattr__
的限制。
示例 3:对象池(复用已有实例)
class DatabaseConnection:
_pool = []
_max_size = 2 # 池中最多保存 2 个实例
def __new__(cls):
# 如果池中有可用实例,直接复用
if len(cls._pool) > 0:
print("复用现有连接")
return cls._pool.pop()
# 否则创建新实例
instance = super().__new__(cls)
return instance
def __init__(self):
# 标记连接是否已初始化
if not hasattr(self, 'initialized'):
print("初始化新连接")
self.initialized = True
# 模拟连接数据库的操作
self.id = id(self)
else:
print("连接已初始化")
def close(self):
# 将实例放回池中
if len(DatabaseConnection._pool) < DatabaseConnection._max_size:
DatabaseConnection._pool.append(self)
print(f"连接 {self.id} 已归还到池中")
# 使用示例
conn1 = DatabaseConnection() # 输出 "初始化新连接"
conn2 = DatabaseConnection() # 输出 "初始化新连接"
conn1.close() # 输出 "连接 140000000 已归还到池中"
conn2.close() # 输出 "连接 140000001 已归还到池中"
conn3 = DatabaseConnection() # 输出 "复用现有连接"
conn4 = DatabaseConnection() # 输出 "复用现有连接"
conn3.close() # 池已满,无法归还
关键点:
• 通过 __new__
控制实例的创建和复用,减少资源开销。
示例 4:动态生成子类实例(工厂模式)
class Animal:
def __new__(cls, animal_type, *args):
# 根据类型返回不同子类的实例
if animal_type == "dog":
return Dog(*args)
elif animal_type == "cat":
return Cat(*args)
else:
raise ValueError("未知动物类型")
class Dog:
def __init__(self, name):
self.name = name
self.sound = "汪汪"
class Cat:
def __init__(self, name):
self.name = name
self.sound = "喵喵"
# 通过父类 Animal 创建不同子类实例
dog = Animal("dog", "小黑")
print(f"{dog.name}: {dog.sound}") # 输出 "小黑: 汪汪"
cat = Animal("cat", "小白")
print(f"{cat.name}: {cat.sound}") # 输出 "小白: 喵喵"
关键点:
• __new__
可以灵活决定返回哪个类的实例,实现类似工厂模式的效果。
示例 5:记录实例创建日志
class LoggedInstance:
_instances = 0
def __new__(cls):
# 每次创建实例时计数
cls._instances += 1
instance = super().__new__(cls)
instance.id = cls._instances
return instance
def __init__(self):
print(f"实例 {self.id} 已创建")
a = LoggedInstance() # 输出 "实例 1 已创建"
b = LoggedInstance() # 输出 "实例 2 已创建"
关键点:
• 在 __new__
中统计实例数量,__init__
中输出日志。
总结
• __new__
的典型场景:单例、不可变对象、参数验证、对象池、动态实例生成。
• __init__
的典型场景:初始化对象属性、配置依赖关系。
• 优先使用 __init__
,仅在需要控制实例创建逻辑时重写 __new__
。
作者:NurDroid