转载须注明出处:简书@Orca_J35
object.__new__(cls[, ...])
在新式类中, __new__
用于控制新实例的创建过程,__init__
用于控制新实例的初始化过程。由于 object 类中已包含 __new__
,所以在每个新式类均拥有该方法。如无特殊说明,本节内容均针对新式类中的 __new__
方法进行讨论。
新式类中的 __new__
是一个特殊的静态方法,无需在声明过程中显式使用 @staticmethod
装饰器。__new__
方法的实参由"类引用"和"构造器实参列表"共同组成。
当我们调用某个类的构造器(constructor)时,便会先调用该类的 __new__
方法。根据 __new__
返回值的不同,可分为以下两种情况:
-
如果返回值是 cls 的实例,之后便会调用该实例的
__init__
方法。__init__
方法的实参由"实例引用"和"构造器实参列表"共同组成,也就是说__new__
和__init__
除了第一个参数不同之外,其余参数均相同。为 cls 类创建新实例的典型方法有以下两种:
- 通过
super()
调用其父类的__new__
方法,如super().__new__(cls[, ...])
。 - 直接调用 object 类的
__new__
方法,如object.__new__(cls)
另外,在
__new__
返回实例前,还可根据需要对已创建的实例进行修改。class A(object): def __new__(cls, a_str): # 特殊静态方法,无需显式使用@staticmethod装饰器 print("into __new__") # object类的__new__方法只需要一个参数,所以只需传递一个参数即可 instance = super().__new__(cls) # 创建实例 instance.num = 61 # 如果需要的话,可对实例进行修改 return instance # 返回该实例 def __init__(self, a_str): print("into __init__") print(self.num) A("hello") # 执行构造器表达式
输出:
into __new__ into __init__ 61
- 通过
-
如果返回值并非 cls 的实例,那么将不会调用该实例的
__init__
方法class Sample(object): def __init__(self): # 不会被调用 print("Sample's __init__") def __str__(self): return "SAMPLE" class A(object): def __new__(cls): """并不会调用Sample或A中的__init__方法""" return super().__new__(Sample) # 返回一个Sample实例 # 也可返回字符串、整数、None、函数对象等,但都不会调用__init__方法 def __init__(self): # 不会被调用 print("A's __init__") print(A())
输出:
SAMPLE
a. Tips
-
在旧式类中并没有
__new__
方法,只有__init__
方法,如果我们定义如下旧类:# -*- coding: utf-8 -*- # python2 class oldStyleClass: def __new__(cls): # 旧式类在构造实例的过程中,不会调用__new__方法 print("into __new__") def __init__(self): print("into __init__") oldStyleClass()
输出结果将会是:
into __init__
可见,旧式类在实例化的过程并不会调用
__new__
方法。 -
只要返回值是 cls 的实例,便会调用该实例的
__init__
方法,无论是否已经调用过:class AbstractClass(object): def __new__(cls, a, b): instance = super(AbstractClass, cls).__new__(cls) instance.__init__(a, b) return instance # 只要返回实例便会调用init,所以这里会调用两次 def __init__(self, a, b): print("into __init__") AbstractClass(1, 2)
输出:
into __init__ into __init__
b. 应用场景
-
当我们继承某个不可变类型(如 int、str、tuple)时,可通过
__new__
方法自定义子类的实例化过程。例如,当我们需要为 int 类创建一个始终存储正数的子类时,便需要覆写__new__
方法:class PositiveInteger(int): def __new__(cls, value): return super(PositiveInteger, cls).__new__(cls, abs(value)) i = PositiveInteger(-6) print(i) # 输出 6
如果我们试图通过重载
__init__
来实现上述功能,会发现在 Python 3 中会抛出异常。class PositiveInteger(int): def __init__(self, value): super(PositiveInteger, self).__init__(abs(value)) # Python3会抛出TypeError # Python2可正常运行,但最终的输出结果仍是 -6 i = PositiveInteger(-6) print(i) # Python2输出-6
-
可以用
__new__
来实现设计模式中的单例模式(singleton)class Singleton(object): def __new__(cls): # 每次实例化的时,均会返回同一个实例对象 if not hasattr(cls, 'instance'): cls.instance = super().__new__(cls) return cls.instance def __init__(self): print("into __init__") # 虽然每次实例化时,均会返回同一个实例对象, # 但是,任然会调用两次__init__方法 inst_1 = Singleton() inst_2 = Singleton() inst_1.attr = 'value' print(inst_1.attr, inst_2.attr) print(inst_1 is inst_2)
输出
into __init__ into __init__ value value True
-
可利用
__new__
来限制实例的总数class LimitedInstances(object): _instances = [] # 存放已有实例引用的列表 limit = 5 def __new__(cls, *args, **kwargs): if not len(cls._instances) <= cls.limit: raise RuntimeError, "Count not create instance. Limit %s reached" % cls.limit instance = object.__name__(cls, *args, **kwargs) cls._instances.append(instance) return instance def __del__(self): # Remove instance from _instances self._instance.remove(self)
-
覆写自定义元类的
__new__
,从而影响类对象的创建过程。# -*- coding: utf-8 -*- class Meta(type): # 自定义元类需继承自type def __new__(cls, name, bases, dct): # __new__方法的参数需和type一致 # 需通过super调用父类的__new__方法来构造新的类对象 x = super().__new__(cls, name, bases, dct) x.attr_of_new = 100 # 在元类中为类对象添加自定义属性 return x # 最后需返回类对象 def __init__(self, name, bases, dct): self.attr_of_init = 200 super().__init__(name, bases, dict)
输出:
>>> class Foo(metaclass=Meta): # 需使用metaclass关键字指定目标元类 ... pass ... >>> Foo.attr_of_new # Meta元类为Foo添加了属性 100 >>> Foo.attr_of_init 200
网友评论