一個案例深入Python中的__new__和__init__


准備

在Python中,一切皆對象。

既然一切皆對象,那么類也是對象,我們暫且稱之為 類對象。來個簡單例子(本篇文章的所有案例都是運行在Python3.4中):

class foo():
    pass

print(id(foo))     
print(type(foo))

# 結果:
# 46627056
# <class 'type'>

如果想深入了解一下,可以看:深刻理解Python中的元類(metaclass)

 

引入

最近在閱讀tornado源碼,發現在其源碼中有很多類是這樣的:

class HTTPServer(TCPServer, Configurable,
                 httputil.HTTPServerConnectionDelegate):

    def __init__(self, *args, **kwargs):
        # Ignore args to __init__; real initialization belongs in
        # initialize since we're Configurable. 就是說默認的__init__初始化方法不在起作用了,改為了initialize方法進行初始化
        pass

或者是干脆沒有__init__ ,只寫了個initialize方法來替代。

所以心生疑惑,tornado是如何做到這一點的?

 

正題

接下來我們來了解一下,Python解釋器是如何創建對象的。

大家可能對Python中的__init__方法很熟悉,認為他是實例化類時調用的第一個方法。但其實他並不是。實例化時調用的第一個方法其實是__new__方法。

 

好了,接下來是重點:

  1  當我們實例化A類對象時,Python中首先調用的是該A類對象的__new__方法,如果該A類對象沒有定義__new__方法,則去父類中依次查找,直到object類

  2  object類有一個__new__方法,該方法接收一個參數(一般為類對象),將該參數進行實例化並返回一個對象

  3  Python解釋器會將調用__new__方法並將A類對象作為第一個參數傳入,最后會返回一個對象(這個對象就是A類的實例對象,我們稱之為a1)

  4  Python解釋器默認會調用a1對象的__init__方法,並將參數傳入。

來一個例子驗證一下:

class asd(object):
    def __new__(cls, *args, **kwargs):
        print('asd.__new__() is running. cls id is %s'%id(cls))
        r = super(asd,cls).__new__(cls)
        print('r_id is %s'%id(r))
        return r


class bnm(asd):

    def __init__(self,name):
        print('bnm.__init__() is running, self id is %s'%id(self))
        self.name = name
        print('bnm.name is %s'%(self.name))

print('asd_id is %s'%id(asd))
print('bnm_id is %s'%id(bnm))
o1 = bnm('ni')
print('o1_id is',id(o1))

# asd_id is 49838320
# bnm_id is 49838768
# asd.__new__() is running. cls id is 49838768
# r_id is 49848400
# bnm.__init__() is running, self id is 49848400
# bnm.name is ni
# o1_id is 49848400
注意 : bnm 和 cls 是同一個對象! r 和 o1 也是同一個對象 !

 

應用

仿tornado實現自定義類的初始化方法:

class asd(object):
    def __new__(cls, *args, **kwargs):
        r = super(asd,cls).__new__(cls)
        r.initialize(*args)
        return r

class bnm(asd):

    def initialize(self):
        print('bnm_initialize is running')

class foo(asd):

    def initialize(self,name):
        self.name = name
        print('foo_initialize is running, my name is %s' %(self.name))


r = bnm()
r1 = foo('linghuchong')

# bnm_initialize is running
# foo_initialize is running, my name is linghuchong
View Code

 

定義類時,只要繼承了asd類,就會將initialize方法作為初始化方法,是不是感覺很(wu)酷(lun)炫(yong)?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM