特殊方法__new__()是一种负责创建类实例的静态方法。它很特殊所以无需使用staticmethod 装饰器将其声明为静态方法。new(cls, [,…])方法的调用优先于__init__()初始化方法。通常来说覆写__new__()的实现将会使用合适的参数调用其超类的 super().new()并在返回之前修改实例class InstanceCountingClass:instances_created 0defnew(cls, *args, **kwargs):print(‘new() called with:’, cls, args, kwargs)instance super().new(cls)instance.number cls.instances_createdcls.instances_created 1return instancedefinit(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 at0x101259e10 abcinstance2 InstanceCountingClass(‘xyz’)__new __() called with: class ’ __main __.InstanceCountingClass’ (‘xyz’,){}__init __() called with: __main.InstanceCountingClass object at0x101259dd8 xyzinstance1.number, instance1.instancescreated(0, 2)instance2.number, instance2.instancescreated(1, 2)通常来说new()方法应该返回该类的一个实例但也可能返回其他类的实例。如果发生了这种情况即返回了其他类的实例那么将会跳过对__init()方法的调用。如果需要修改不可变类实例例如 Python 的某些内置类型的创建行为那么这一点是很有用的如下所示class NonZero(int):defnew(cls, value):return super().new(cls, value) if value ! 0 else Nonedefinit(self, skipped_value):在这个例子中可以跳过__init__的实现但放在这里是为了展示它如何不被调用print(“init() called”)super().init()我们在交互式会话中查看运行结果如下type(NonZero(-12))__init() calledclass ’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()方法是非常合理的。那就是下一节将介绍的元类。
Python 高手编程系列三千四百二十:使用 __new __()方法覆写实例创建过程
特殊方法__new__()是一种负责创建类实例的静态方法。它很特殊所以无需使用staticmethod 装饰器将其声明为静态方法。new(cls, [,…])方法的调用优先于__init__()初始化方法。通常来说覆写__new__()的实现将会使用合适的参数调用其超类的 super().new()并在返回之前修改实例class InstanceCountingClass:instances_created 0defnew(cls, *args, **kwargs):print(‘new() called with:’, cls, args, kwargs)instance super().new(cls)instance.number cls.instances_createdcls.instances_created 1return instancedefinit(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 at0x101259e10 abcinstance2 InstanceCountingClass(‘xyz’)__new __() called with: class ’ __main __.InstanceCountingClass’ (‘xyz’,){}__init __() called with: __main.InstanceCountingClass object at0x101259dd8 xyzinstance1.number, instance1.instancescreated(0, 2)instance2.number, instance2.instancescreated(1, 2)通常来说new()方法应该返回该类的一个实例但也可能返回其他类的实例。如果发生了这种情况即返回了其他类的实例那么将会跳过对__init()方法的调用。如果需要修改不可变类实例例如 Python 的某些内置类型的创建行为那么这一点是很有用的如下所示class NonZero(int):defnew(cls, value):return super().new(cls, value) if value ! 0 else Nonedefinit(self, skipped_value):在这个例子中可以跳过__init__的实现但放在这里是为了展示它如何不被调用print(“init() called”)super().init()我们在交互式会话中查看运行结果如下type(NonZero(-12))__init() calledclass ’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()方法是非常合理的。那就是下一节将介绍的元类。