|
在构建诸如数字和字符串这样的内建类型的子类型时,或在其它别的一些场合时,会用到静态方法__new__。__new_先于__init__被调用,__new__是实例化对象的第一步。__new__的第一个参数就是类本身,其作用是用于返回类的一个实例。而__init__则没有返回。调用__init__时其第一个参数是实例本身。__init__的作用是初始化实例。存在着调用__new__来创建实例,而不需要用__init__来初始化实例的情况。但是却没有创建新的实例而不调用__new__的情况。
需要知道,调用类来创建类实例的过程是调用类的__new__,并将类作为第一个参数。当返回类实例后,调用__init__来初始化该类实例。(这个过程是由metaclass的__call__来控制的)
下面的代码片段显示了override __new__的作用:
class inch(float):
"Convert from inch to meter"
def __new__(cls, arg=0.0):
return float.__new__(cls, arg*0.0254)
调用如下:
print inch(12) //输出0.3048
对比一下override __init__的情况:
class inch(float):
def __init__(self, arg=0.0):
float.__init__(self, arg*0.0254)
Override __init__方法并不工作,因为float的__init__并不是一个操作:其会立即返回,忽略输入的参数。对于float这样的不可变内建类型,其__init__是dummy __init__,并没有任何实质操作。
(这样一来,那些不可变的类型能够保持其不可更改性,同时允许创建子类型。float类型的实例由其__init__进程初始化,可以通过调用__init__来改变已经存在的float对象的值。例如:
>>> # THIS DOESN'T WORK!!!
>>> import math
>>> math.pi.__init__(3.0)
>>> print math.pi
3.0
>>>
)
注意:在Python 2.7中,已经不能通过调用__init__来改变已经存在的实例对象了。
>>> import math
>>> print math.pi
3.14159265359
>>> math.pi.__init__(3.0)
>>> print math.pi
3.14159265359
>>>
下边是使用__new__的一些规则:
(1)__new__是静态方法
(2)传递给__new__的第一个参数必须是类,剩下的参数与构建方法相同
(3)override __new__方法会调用基类的__new__,而基类的__new__的第一个参数应该也是override __new__的类,而不是基类。
(4)一般情况下,子类的__new__的第一个参数是该类本身;如果需要,可以通过如下1)传递不同的参数给基类2)在结果对象创建后对其进行修改这两种方法来影响最终的返回对象
(5)__new__必须返回一个对象。通常情况下当然是返回新的子类对象。如果返回的是子类或其基类的已经存在的对象,则构建函数仍然会调用__init__。如果返回不同类的对象,则不会调用__init__。如果没有返回,Python不会将其设置为None
注意:在Python 2.7中,如果没有返回,Python会将其设置为None
例如
class inch2(float):
"Convert from inch to meter"
def __new__(cls, arg=0.0):
noreturn = float.__new__(cls, arg*0.0254)
测试:p = inch2(12)
则p为None
(7)如果创建不可变类型的子类型,并且想加入一些可变的状态的话,最好是在__init__中添加,而不是在__new__中添加
(8)如果想要改变构造函数的signature,则需要同时override __new__和__init__。多数内建类型忽略传递给它们而它们并不使用参数。不可变类(如int, long, float, complex, str, unicode,和tuple)有一个dummy__init__,而可变类(如dict, list, file,staticmethod等)则有一个dummy __init__.内建的类型”object”则有dummy __new__ 和dummy __init__.
|
|