Python __new__ 方法
最后修改于 2025 年 3 月 25 日
本综合指南深入探讨了 Python 的 __new__ 方法,这是在调用 __init__ 之前负责对象创建的特殊方法。我们将通过详细的示例涵盖其目的、用例和高级模式。
理解 __new__ 基础知识
__new__ 方法是一个静态方法,它创建并返回类的新实例。它在 __init__ 之前被调用,负责实际的对象创建,而 __init__ 负责初始化。
basic_new.py
class Example:
def __new__(cls, *args, **kwargs):
print("__new__ called")
instance = super().__new__(cls)
return instance
def __init__(self, value):
print("__init__ called")
self.value = value
obj = Example(10)
在这个基本示例中,我们看到了创建对象时的操作顺序。__new__ 方法
- 接收类作为其第一个参数(cls)
- 使用
super().__new__(cls)创建实例 - 返回新实例,然后该实例会传递给
__init__
__new__ 的关键特征
- 它是一个静态方法(虽然不需要装饰器)
- 必须返回一个实例(通常是 cls 的实例)
- 可以返回其他类的实例(与
__init__不同) - 在常规类中很少需要重写
定制对象创建
__new__ 方法允许对实例创建进行完全控制。此示例展示了如何定制创建的内容。
custom_creation.py
class CustomObject:
def __new__(cls, value):
if value < 0:
return None # Return None for negative values
instance = super().__new__(cls)
instance.created_at = time.time()
return instance
def __init__(self, value):
self.value = value
obj1 = CustomObject(5) # Creates instance
obj2 = CustomObject(-1) # Returns None
此示例演示了几个重要概念
- 我们可以通过返回非我们类实例的内容来完全绕过正常的实例创建
- 我们可以在调用
__init__之前向实例添加属性 - 只有当
__new__返回类实例时,__init__方法才会运行
实际应用包括
- 对象创建前的输入验证
- 向实例添加创建元数据
- 实现对象池或缓存
实现单例模式
__new__ 方法非常适合实现单例模式,该模式确保类只有一个实例。
singleton.py
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.initialized = False
return cls._instance
def __init__(self):
if not self.initialized:
print("Initializing Singleton")
self.initialized = True
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # True
此实现通过以下方式保证只有一个实例存在
- 将单个实例存储在类变量
_instance中 - 在创建新实例之前检查实例是否存在
- 使用
initialized标志来防止__init__运行多次
重要注意事项
initialized标志可防止重新初始化- 线程安全的实现需要额外的锁定
- 子类化单例需要特别注意
创建不可变对象
__new__ 可用于通过控制属性赋值来创建不可变对象。
immutable.py
class ImmutablePoint:
__slots__ = ('x', 'y') # Prevents dynamic attribute creation
def __new__(cls, x, y):
instance = super().__new__(cls)
instance.x = x # Allowed during creation
instance.y = y
return instance
def __setattr__(self, name, value):
raise AttributeError(f"Cannot modify {name}")
p = ImmutablePoint(3, 4)
print(p.x, p.y) # Works
p.x = 5 # Raises AttributeError
此不可变实现结合了多种技术
__slots__可防止添加新属性__new__在创建期间设置初始值__setattr__阻止后续修改
为什么有效
- 属性可以在
__new__期间设置,之后__setattr__才生效 __slots__使对象更节省内存- 组合创建了一个真正不可变的对象
子类化内置类型
子类化元组或字符串等不可变内置类型时,__new__ 至关重要。
subclass_tuple.py
class NamedTuple(tuple):
def __new__(cls, items, name):
instance = super().__new__(cls, items)
instance.name = name
return instance
def __init__(self, items, name):
# __init__ is still called but often unused in these cases
pass
nt = NamedTuple([1, 2, 3], "My Numbers")
print(nt) # (1, 2, 3)
print(nt.name) # "My Numbers"
子类化不可变类型时
__new__必须完成所有工作,因为对象在创建后是不可变的- 我们将不可变数据传递给父类的
__new__ - 附加属性必须在对象变为不可变之前在
__new__中设置
常见用例
- 向内置类型添加元数据
- 创建字符串、数字或元组的专用版本
- 实现自定义不可变集合
对象池模式
__new__ 可以实现对象池,以重用实例而不是创建新实例。
object_pool.py
class DatabaseConnection:
_pool = {}
_max_pool_size = 3
def __new__(cls, connection_string):
if connection_string not in cls._pool:
if len(cls._pool) >= cls._max_pool_size:
raise RuntimeError("Connection pool exhausted")
instance = super().__new__(cls)
instance._connect(connection_string)
cls._pool[connection_string] = instance
return cls._pool[connection_string]
def _connect(self, connection_string):
print(f"Connecting to {connection_string}")
self.connection_string = connection_string
conn1 = DatabaseConnection("db1.example.com")
conn2 = DatabaseConnection("db1.example.com") # Returns same instance
此对象池实现
- 维护现有连接的字典
- 为相同的连接字符串返回现有实例
- 仅在需要时创建新连接
- 强制执行最大池大小
对象池的好处
- 减少资源密集型对象创建
- 限制昂贵资源(如数据库连接)的总数
- 提供实例的集中管理
元类 __new__ 方法
在元类中,__new__ 控制类创建(而不是实例创建)。
meta_new.py
class MetaLogger(type):
def __new__(mcls, name, bases, namespace):
print(f"Creating class {name}")
# Add a class-level logger
namespace['logger'] = logging.getLogger(name)
return super().__new__(mcls, name, bases, namespace)
class User(metaclass=MetaLogger):
pass
print(User.logger) #
元类 __new__ 与常规 __new__ 不同
| 常规 __new__ | 元类 __new__ |
|---|---|
| 创建实例 | 创建类 |
| 接收 cls | 接收 mcls(元类) |
| 返回实例 | 返回类 |
常见的元类用途
- 类注册系统
- 自动添加方法/属性
- 强制执行编码标准
- API 端点生成
最佳实践和陷阱
在使用 __new__ 时,请记住这些指南
- 不要不必要地重写
__new__- 大多数类只需要__init__ - 始终调用 super().__new__ - 除非您有特定原因不这样做
- 请记住
__init__可能不会运行 - 如果__new__返回现有实例 - 记录您的
__new__行为 - 对其他开发人员来说不明显 - 考虑线程安全性 - 对于单例或对象池等模式
结论
__new__ 方法在 Python 中提供了对对象创建的低级控制。虽然日常编程不需要它,但理解 __new__ 对于像以下高级 Python 模式至关重要:
- 自定义不可变对象
- 单例实现
- 对象池
- 子类化内置类型
- 元类编程
当您需要精确控制实例创建时,请谨慎使用它,但首选 __init__ 进行常规初始化任务。
资料来源
作者
列出所有 Python 教程。