Python __new__() 语法
__new__() 是 Python 的一个特殊方法(通常称为 “魔法方法” 或 “双下划线方法”), 它在类的新实例被创建之前调用,负责对象的实际创建。
语法:
class MyObject:
def __new__(cls, *args, **kwargs):
# 创建并返回一个新实例
instance = super().__new__(cls)
# 可选:在实例返回前进行一些操作
return instance
说明:
__new__() 方法接收以下 2 种参数。
cls(必选):约定俗成的名称,代表正在创建实例的类本身。*args, **kwargs(可选):传递给类构造函数(MyObject(…))的所有其他位置参数和关键字参数。这些参数也会传递给 __init__()。
__new__() 方法是 Python 中真正的构造器。它是一个类方法,即使没有 @classmethod 装饰器,它也会自动接收类作为第一个参数。
__new__() 必须返回一个新创建的实例对象,通常是 cls 的实例。如果它返回 cls 的实例,那么该实例的 __init__() 方法将自动调用。如果它返回其他类的实例,则不会调用 __init__() 方法。
提示:
- __new__() 是在 __init__() 之前执行的。
- __new__() 的主要功能是创建并返回一个空对象,而 __init__() 的职责是初始化这个空对象。
- __new__() 常用于实现单例模式、不可变对象、元类等高级用法。
Python __new__() 摘要
| 属于 | Python 魔法方法 |
|---|---|
| 使用频率 | 低 |
| 官方文档 | 查看 |
| 相关函数 | __init__() |
Python __new__() 示例
接下来,我们通过几个简单的例子来讲解一下 Python __new__() 方法是如何使用的。
示例 1:__new__() 的基本工作流
# 定义类
class MyObject:
def __new__(cls, *args, **kwargs):
print('__new__() 方法被调用')
# 实际创建对象
instance = super().__new__(cls)
return instance
def __init__(self, name):
print('__init__() 方法被调用')
self.name = name
def __str__(self):
return f'MyObject(name={self.name})'
# 创建实例
obj = MyObject('绿叶网')
运行结果如下。
__new__() 方法被调用
__init__() 方法被调用
分析:
当 MyObject('绿叶网') 被调用时,首先执行的是 __new__() 方法,它负责创建实际的对象。然后 __init__() 方法被调用,对这个新创建的对象进行初始化(设置 name 属性)。
需要注意的是,__new__() 接收的是 cls ( 类 ),而 __init__() 接收的是 self ( 实例 )。
示例 2:使用 __new__() 实现单例模式
class Singleton:
# 用于存储单例实例
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
# 创建 Singleton 的新实例
cls._instance = super().__new__(cls)
# 返回现有 Singleton 实例
return cls._instance
def __init__(self, value):
# 只有在第一次创建时才初始化,避免重复初始化已存在的实例
if not hasattr(self, '_initialized'):
self.value = value
self._initialized = True # 标记为已初始化
def get_value(self):
return self.value
# 第一次获取实例
s1 = Singleton('First')
print(s1.get_value())
print(id(s1))
# 第二次获取实例
s2 = Singleton('Second') # __init__() 仍然会被调用,但我们只在第一次初始化
print(s2.get_value())
print(id(s2))
# 验证它们是否是同一个实例
print(s1 is s2)
运行结果如下。
First
2384401493568
First
2384401493568
True
分析:
单例模式会确保一个类只有一个实例,并提供一个全局访问点。__new__() 方法非常适合用于实现这一模式。
在 Singleton 类中,__new__() 方法被重写以控制实例的创建。它会判断 _instance 类属性是否为 None。如果是,则创建新实例并存储起来;如果不是,则直接返回已存在的实例。这样就确保了无论调用多少次 Singleton(),都只会得到同一个实例。
需要注意的是,__init__() 每次都会被调用。因此在单例模式中,__init__() 通常需要额外的逻辑来防止重复初始化。
示例 3:__new__() 返回不同类的实例
class MyClassA:
def __new__(cls, switch):
if switch == 'B':
# __new__ 返回 MyClassB 实例
return MyClassB('从 A 切换到 B')
else:
# __new__ 返回 MyClassA 实例
return super().__new__(cls)
# MyClassA 的 __init__() 被调用
def __init__(self, name):
self.name = name
def get_name(self):
return f'A: {self.name}'
class MyClassB:
# MyClassB 的 __init__() 被调用
def __init__(self, description):
self.description = description
def get_description(self):
return f'B: {self.description}'
# 创建 MyClassA 实例
objA = MyClassA('A')
print(type(objA), objA.get_name())
# 切换到创建 MyClassB 实例
objB = MyClassA('B') # 看起来像是创建 MyClassA,但实际返回了 MyClassB
print(type(objB), objB.get_description())
运行结果如下。
<class '__main__.MyClassA'> A: A
<class '__main__.MyClassB'> B: 从 A 切换到 B
分析:
__new__() 甚至可以返回不同于 cls 的实例。如果 __new__() 返回的不是 cls 的实例,那么该对象的 __init__() 方法将不会被调用。
当 MyClassA('B') 被调用时,MyClassA 的 __new__() 方法被触发,它会根据 switch 参数的判断,最终返回了一个 MyClassB 的实例。在这种情况下,MyClassA 的 __init__() 方法就不会对这个 MyClassB 实例进行初始化。
