Python 高手编程系列一十五:使用 __new __()方法覆写实例创建过程

特殊方法__new__()是一种负责创建类实例的静态方法。它很特殊,所以无需使用

staticmethod 装饰器将其声明为静态方法。new (cls, ,...)方法的调用优先

于__init__()初始化方法。通常来说,覆写__new__()的实现将会使用合适的参数调用其

超类的 super().new (),并在返回之前修改实例:

class InstanceCountingClass:

instances_created = 0

def new (cls, *args, **kwargs):

print('new () called with:', cls, args, kwargs)

instance = super().new (cls)

instance.number = cls.instances_created

cls.instances_created += 1

return instance

def init (self, attribute):

print('init () called with:', self, attribute)

self.attribute = attribute

下面是在交互式会话中的日志示例,展示了 InstanceCountingClass 实现的工作

方式:

instance1 = InstanceCountingClass('abc')

__new __() called with: <class ' __main __.InstanceCountingClass'> ('abc',)

{}

__init __() called with: < __main __.InstanceCountingClass object at

0x101259e10> abc

instance2 = InstanceCountingClass('xyz')

__new __() called with: <class ' __main __.InstanceCountingClass'> ('xyz',)

{}

__init __() called with: < __main .InstanceCountingClass object at
0x101259dd8> xyz
instance1.number, instance1.instances
created
(0, 2)
instance2.number, instance2.instances
created
(1, 2)
通常来说,new ()方法应该返回该类的一个实例,但也可能返回其他类的实例。
如果发生了这种情况(即返回了其他类的实例),那么将会跳过对__init
()方法的调用。

如果需要修改不可变类实例(例如 Python 的某些内置类型)的创建行为,那么这一点是很

有用的,如下所示:

class NonZero(int):

def new (cls, value):

return super().new (cls, value) if value != 0 else None

def init(self, skipped_value):

在这个例子中可以跳过__init__的实现

但放在这里是为了展示它如何不被调用

print("init () called")

super().init ()

我们在交互式会话中查看运行结果如下:

type(NonZero(-12))

__init () called
<class ' main .NonZero'>
type(NonZero(0))
<class 'NoneType'>
NonZero(-3.123)
init () called
-3
那么什么情况下使用__new
()呢?答案很简单:只有在__init
()不够用的时候。
前面已经提到了这样的一个例子,就是对 Python 不可变的内置类型(如 int、str、float、frozenset 等)进行子类化。这是因为一旦创建了这样不可变的对象实例,就无法在
init ()方法中对其进行修改。
有些程序员可能会认为,new ()对执行重要的对象初始化可能很有用,如果用户
忘记使用 super(),可能会漏掉这一初始化。init ()调用是覆写的初始化方法。虽
然这听上去很合理,但却有一个主要的缺点。如果使用这样的方法,那么即使初始化过程
已经是预期的行为,程序员明确跳过初始化步骤也会变得更加困难。它还破坏一条潜规则,
即在__init
()中执行所有的初始化工作。
由于__new
()不限于返回同一个类的实例,所以很容易被滥用。不负责任地使用这
种方法,可能会对代码有害,因此始终应该谨慎使用,并且提供大量文档来支持。一般来
说,对于特定问题,最好搜索其他可用的解决方法,而不要影响对象创建过程,使其违背
基础程序员的预期。即使是上文提到的覆写不可变类型的初始化的例子,也可以用可预测
性更高且更加完善的设计模式来替代,例如第 14 章介绍的工厂方法。
在 Python 编程中,至少在一个方面大量使用__new
()方法是非常合理的。那就是下

一节将介绍的元类。

相关推荐
for_ever_love__6 分钟前
LangChain Token 计数与成本控制:先知道钱花在哪
开发语言·langchain
4SAPI6 分钟前
Provider Adapter设计模式:如何让AI应用支持多个模型
网络·人工智能·设计模式
m0_547486667 分钟前
《数据结构 (C语言)》期末试卷及答案(10套)2026版
c语言·开发语言·数据结构
顽疲8 分钟前
Java vue 养老系统源码详解:入住核算单 live-check 缴费入账与预交金收据实践
java·vue.js·python·java 养老·springboot 养老·vue养老
Sand(ContextGate)10 分钟前
从开发者调试工具到消费级 App:一个 AI 电商导购项目的 C 端改造与 6 个连环踩坑实录
开发语言·人工智能·python·ai·langchain
外收内放14 分钟前
数据分析(DataFrame与series)
python·学习·数据分析
白杨尚青15 分钟前
C++入门篇(十七):priority_queue 完整模拟实现——仿函数、区间建堆与指针特化(附完整代码仓库)
开发语言·c++·stl·仿函数
j7~27 分钟前
【仿 RabbitMQ 项目】发布订阅消息队列 (篇八):交换机数据管理模块,分层类设计、SQLite 回调原理与代码实现
服务器·网络·数据库·消息队列·交换机·交换机数据管理·sqlite回调原理
电梯界知识分子28 分钟前
浙江台州六层拆迁自建房:建房前期土建交底,规避薄墙预制板误区,预留轮椅通行电梯
运维·服务器·网络·安全·家用电梯
Escalating_xu29 分钟前
【Linux 网络编程】UDP Socket 编程(下):回调封装、网络字典与多线程聊天室
linux·网络·udp