1. 程式人生 > >python中init()方法和new()方法的區別

python中init()方法和new()方法的區別

new()理解:

class A(object):
    def __init__(self,*args,**kwargs):
        print "init &&&&  %s" % self.__class__
    def __new__(cls,*args,**kwargs):
        print "new &&&&  %s" % cls
        return object.__new__(cls,*args,**kwargs)
a=A()
輸出結果為:
new &&&&  <class
'__main__.A'>
init &&&& <class '__main__.A'> 如果把最後一行的return程式碼遮蔽掉,輸出結果為: new &&&&<class '__main__.A'>

new()是在新式類中新出現的方法,它作用在構造方法init()建造例項之前,可以這麼理解,在Python 中存在於類裡面的構造方法init()負責將類的例項化,而在init()呼叫之前,new()決定是否要使用該init()方法,因為new()可以呼叫其他類的構造方法或者直接返回別的物件來作為本類 的例項。

new()方法的特性:
new()方法是在類準備將自身例項化時呼叫。
new()方法始終都是類的靜態方法,即使沒有被加上靜態方法裝飾器。

Python首先呼叫new()方法:

第一個引數cls是當前正在例項化的類。

def __new__(cls, *args, **kwargs):
   ...

如果要得到當前類的例項,應當在當前類中的new()方法語句中呼叫當前類的父類 的new()方法。
例如,如果當前類是直接繼承自object,那當前類的new()方法返回的物件應該為:

def __new__(cls, *args, **kwargs):
   ...
   return
object.__new__(cls)

事實上如果(新式)類中沒有重寫new()方法,即在定義新式類時沒有重新定義new()時 ,Python預設是呼叫該類的直接父類的new()方法來構造該類的例項,如果該類的父類也沒有重寫 new(),那麼將一直按此規矩追溯至object的new()方法,因為object是所有新式類的基類。
而如果新式類中重寫了new()方法,那麼你可以自由選擇任意一個的其他的新式類(必定要是 新式類,只有新式類必定都有new(),因為所有新式類都是object的後代,而經典類則沒有new() 方法)的new()方法來製造例項,包括這個新式類的所有前代類和後代類,只要它們不會造成遞迴死 迴圈。具體看以下程式碼解釋:

class Foo(object):
    def __init__(self, *args, **kwargs):
        ...
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)    

# 以上return等同於 
# return object.__new__(Foo, *args, **kwargs)

class Child(Foo):
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)

如果Child中沒有定義new()方法,那麼會自動呼叫其父類的new()方法來製造例項,即

Foo.__new__(cls, *args, **kwargs)

使用object或者沒有血緣關係的新式類的new()是安全的,但是如果是在有繼承關係的兩個類之間,應避免互調造成死迴圈,例如:(Foo)return Child.new(cls), (Child)return Foo.new(cls)。

關於”new”方法還有一個重要的用途就是用來派生不可變型別 。

例如,python中的float型別是一個不可變型別,如果想要從float中派生出一個子類,就可以使用”new”方法:

class Round2Float(float):
    def __new__(cls,num):
        num = round(num,2)
        obj = float.__new__(Round2Float,num)
        return obj

f=Round2Float(4.324599)
print f

init理解:

new”決定是否要使用該類的”init”方法,因為”new” 可以呼叫其他類的構造方法或者直接返回別的類建立的物件來作為本類的例項。
通常來說,新式類開始例項化時,”new”方法會返回cls(cls指代當前類)的例項,然後呼叫該類的”init”方法作為初始化方法,該方法接收這個例項(即self)作為自己的第一個引數,然後依次傳入”new”方法中接收的位置引數和命名引數。
但是,如果”new”沒有返回cls(即當前類)的例項,那麼當前類的”init”方法是不會被呼叫的。例子:

class A(object):
    def __init__(self,*args,**kwargs):
        print "calling __init__ from %s" % self.__class__

    def __new__(cls,*args,**kwargs):
        obj = object.__new__(cls,*args,**kwargs)
        print "calling __new__ from %s" % obj.__class__
        return obj

class B(A):
    def __init__(self,*args,**kwargs):
        print "calling __init__ from %s" % self.__class__
    def __new__(cls,*args,**kwargs):
        obj = object.__new__(A,*args,**kwargs)
        print "calling __new__ from %s" % obj.__class__
        return obj

b=B()
print type(b)
結果是:
calling __new__ from <class '__main__.A'>
<class '__main__.A'>

程式碼中,在B的”new”方法中,通過”obj = object.new(A, *args, kwargs)”建立了一個A的例項,在這種情況下,B的”init”函式就不會被呼叫到。

區別:

對於”new”和”init”可以概括為:
new”方法在Python中是真正的構造方法(建立並返回例項),通過這個方法可以產生一個”cls”對應的例項物件,所以說”new”方法一定要有返回。
對於”init”方法,是一個初始化的方法,”self”代表由類產生出來的例項物件,”init”將對這個物件進行相應的初始化操作。