python當中__metaclass__探討

最初博主是但願在python當中建立一個單列模式的類,由於python當中不像java和php當中有權限修飾符(private),因此實現起來要繞一點。php

網上找了一下python實現單列模式,相似的大概有這種方法:java

class singleton(type):
    """
    實現單列模式的元類
    總之,metaclass的主要任務是:
    攔截類,
    修改類,
    返回類
    """

    def __init__(cls,classname,parrentstuple,attrdict):
        """
        """
        super(SigleInstance,cls).__init__(classname,parrentstuple,attrdict)
        cls._instance = None

    def __call__(self,*args,**kargs):
        """
        """
        if self._instance:
            return self._instance
        else:
            self._instance = super(SigleInstance,self).__call__(*args,**kargs)
            return self._instance

這就是單列的元類,我把它小寫了,由於type也是小寫的。而後呢,在即將要實現單列的class當中這樣寫:python

class Earth(object):
    __metaclass__ = singleton
    def __init__(self,a,b):
        pass

這樣每次 建立一個 Earth()取得的始終都應該是一個實例。python2.7

關於__metaclass__ 和type這個東西能夠參考深刻理解Python中的元類(metaclass)。這篇文章解決了我大部分的疑惑,可是我仍是沒有搞清楚的是:函數

當__metaclass__是一個類的時候,metaclass是怎樣去建立一個類的?spa

在這以前首先說明一下:.net

一。python當中一切都是對象,而對象都是由類建立,這裏爲了區分概念,咱們不妨換一種說法:實例都是由模板建立的。code

 

二。那麼什麼又是對象的type呢?type就是類型的意思。若是您對java稍微有一點了解。你會有這樣的認識:htm

/**
* language是一個String類型的變量,值爲"python"
* 在java當中,若是 Integer language = "python"就是錯誤的
*/
String language = "python";

因爲python是一門動態語言(變量無類型,變量的值纔有類型),python當中變量的值同樣是有類型的。那麼怎麼來看變量值的類型呢?答案是使用type。對象

language = "python"
print type(language)
# python2.7中輸出:<type 'str'>
# ipython 輸出 str

number = 2
print type(number)
#輸出:<type 'int'>

class A(object):
    pass

a = A()
print type(a)
#輸出:<type '__main__.A'>

上面段代碼分別用type查看到了各個變量的類型。根據(一)【python當中一切都是對象,而對象都是由類建立,這裏爲了區分概念,咱們不妨換一種說法:實例都是由模板建立的】咱們可不能夠這樣說呢:language是str的實例,str是language實例的模板。所以type(a_var)的輸出就是a_var這個實例的模板。因此咱們看到 type(a)的輸出是<type '__main__.A'>,也就是說 a實例的模板是A。

class A的模板是什麼呢?

print type(A)
#輸出:<type 'type'>

也就是說,一個類的模板的type,類是type的一個實例,tpye實例化了一個對象,這個對象就是class。因此在python的世界裏,一切都是對象,類也是對象。

那麼有意思的地方來了,type的模板是什麼呢,也就是,type的type是什麼呢?

print type(type)
# 輸出<type 'type'>

是否是頗有意思,type的type是type。很繞,換成大白話說:type的模板是type本身。那麼是否是就能夠這樣說呢?TYPE(type,爲了區分說明,故意大寫)是type的模板,type是TYPE的實例,所以說明type是一個實例;而TYPE是一個模板,也就是一個類!,由於TYPE==type,那麼能夠得出結論:

type是一個類(class),type也是自身的實例(instance)

python當中一切都是對象,類也是對象,對於type來講,更爲特殊,由於type的模板是type,也就是說,type本身建立了本身,type是自身的實例。

 

三。實例是由類模板建立(也就是咱們平時所寫的class),而類是由元類模板建立(就是__metaclass__指定的類)。因此【元類和類的關係】就相似於【實例和類的關係】。

根據博主所探討的結果代表,__metaclass__在建立類的過程大概是這樣的:當類Earth的實例 earth正要被建立的時候,

  1. 查找Earth當中是否有__metaclass__,若是沒有查找父類是否有__metaclass__,若是沒有找到,就看包當中是否有__metaclass__,若是仍是沒有,那直接使用type建立該類。若是找到了,就用該__metaclass__來建立類。

  2. 那麼若是找了__metaclass__,那麼系統首先建立一個__metaclass__的實例,而這個由metaclass建立的實例正好的一個 Earth類,注意是Earth類(class),而不是一個Earth的一個實例哦。

那麼到這一步究竟發生了些什麼呢?咱們寫幾行代碼來看一看:

#!/usr/bin/env python
#-*-coding:utf-8-*-

# author : "qiulimao"
# email  : "qiulimao@getqiu.com"

""" 
 the module's duty
""" 
#---------- code begins below -------

class SimpleMetaClass(type):
    
    def __init__(self,*args,**kwargs):
        print "you have create a class instance by metaclass"
        super(SimpleMetaClass,self).__init__(*args,**kwargs)

class Earth(object):
    
    __metaclass__ = SimpleMetaClass

    def sayHello():
        print "hello world"


if __name__ == "__main__":
    
    print "do something that have nothing with SimpleMetaClass and Earth"

最後運行的結果是這樣的:

you have create a class instance by metaclass                 #①
do something that have nothing with SimpleMetaClass and Earth #②

經過這個小例子咱們看到:咱們並無使用過 Earth類,也沒有使用過SimpleMetaClass這個元類,但實際的結果看來,SimpleMetaClass這個模板確被使用過了,由於打印出了①,後面咱們會知道,打印出①是由於python使用SimpleMetaClass模板來建立出了Earth這個類對象(不是Earth的一個實例)。這個過程咱們能夠用咱們日常常常說的一句話來描述:這個步驟至關於實例化了一個metaclass(SimpleMetaClass)對象,而這個對象正好是Earth類。

那麼這裏確定會有人問了:我平時寫class的時候都是不帶__metaclass__的啊?那是由於若是你不寫__metaclass__,最終這個類的模板就是type。上面的代碼能夠看到SimpleMetaClass是繼承自type的。

四。Earth類已經被metaclass所建立出來了,那麼當實例化一個Earth類(也就是建立一個earth對象)的時候又發生了什麼呢?

在說明這個問題以前,咱們得先聊一聊__call__,__new__這兩個特殊方法。對於一個實現了__call__的類,那麼它的實例能夠當作函數來調用。來看個例子:

class MagicCall(object):

    def __new__(cls,name):
        return super(MagicCall,cls).__new__(cls)

    def __init__(self,name):
        self.name=name

    def __call__(self):
        print "you have invoked __call__ method...."

if __name__ == '__main__':
    magicCall = MagicCall("python")
    magicCall()

#輸出的結果爲:you have invoked __call__ method....

而__new__有攔截類實例化的功能,在建立一個對象的過程當中,執行__init__方法時,解釋器已經爲對象分配了內存,實例已經存在了,__init__方法只是改變這個類當中的某些參數。而在執行__new__方法時,這個實例是不存在的,而__new__就是要建立這個實例,因此__new__必需要有返回值。

如今咱們回過頭來想想:爲何建立 一個類的實例是這種寫法:

instance = SomeClass()
instance = SomeClass(args1,args2,...)

回答這個問題,咱們能夠用元類來解釋。咱們知道類是元類的一個對象,而元類的實例都有一個__call__方法。擁有__call__方法的對象能夠把對象當作一個函數調用。因此嘍,咱們在建立一個類的實例的時候,其實是調用了類對象的__call__(MetaClass:__call__)這個方法。

來看一個比較長的例子:

#!/usr/bin/env python
#-*-coding:utf-8-*-

# author : "qiulimao"
# email  : "qiulimao@getqiu.com"

""" 
 the module's duty
""" 
#---------- code begins below -------

class SimpleMetaClass(type):
    
    def __new__(cls,*args,**kwargs):
        print "creating class Earth..."
        return  super(SimpleMetaClass,cls).__new__(cls,*args,**kwargs)

    def __init__(self,*args,**kwargs):
        print "you have create a class instance by metaclass"
        super(SimpleMetaClass,self).__init__(*args,**kwargs)

    def __call__(self,*args,**kwargs):
        print "__call__ in metaclass has been invoked...","the args:",args,kwargs
        return super(SimpleMetaClass,self).__call__(*args,**kwargs)

    
class Earth(object):
    
    __metaclass__ = SimpleMetaClass

    def __new__(cls,g,R=65535):
        print "creating instance using __new__"
        cls.g = g
        cls.R = R
        return super(Earth,cls).__new__(cls);

    def __init__(self,g,R=65535):
        print "initializing instance in __init__"
        print "gravity on Earth is:%f" % self.g

    def __call__(self):
        print self.g 

    def sayHello(self):
        print "hello earth,your gravity is:%f" % self.g


if __name__ == "__main__":
    
    earth = Earth(9.8,R=65535)
    earth()
    earth.sayHello()

不知道大衆喜歡在代碼中寫註釋的方式來說解,仍是直接寫文字過程。我就寫文字過程吧。

最終上面這段代碼執行的結果是:

①creating class Earth...
②you have create a class instance by metaclass
③__call__ in metaclass has been invoked... the args: (9.8,) {'R': 65535}
④creating instance using __new__
⑤initializing instance in __init__
⑥gravity on Earth is:9.800000
⑦9.8
⑧hello earth,your gravity is:9.800000

咱們來慢慢分析。

  1. 首先python建立SimpleMetaClass類,這個SimpleMetaClass是元類,應該是由type建立的。
  2. 當建立Earth這個類時,找到了它類中有__metaclass__屬性,因而,採用SimpleClass來建立這個類
  3. 建立Earh類時,解釋器會把類名,父類元祖,類的屬性三個參數傳給SimpleMetaClass
  4. SimpleMetaClass 根據 clazzName,(parent2,parent1,..),{'attribute':....,'method':''}在本身__new__方法中建立出這個Earth實例【打印出①】,而後調用本身的__init__方法初始化類的參數【打印出②】。這時,這個Earth類做爲metaclass的一個實例就被建立好了。
  5. 接下來經過 earth = Earth(9.8,R=65535) 建立一個Earth對象實例earth。這一步其實是調用 Earth這個類對象的__call__(SimpleMetaClass::__call__)方法來建立一個Earth的實例。【打印出③,咱們還能看到調用__call__的參數】。
  6. 而建立earth實例的方法__new__(Earth::__new__),和__init__(Earth:__init__),將會在Earth實例中的__call__(SimpleMetaClass::__call__)當中前後得以執行【前後打印出④⑤⑥】執行完成Earth實例earth對象被返回。
  7. 我想⑦⑧你們應該很容易理解了。

以上就是我對元類的理解,其中若有錯誤的地方還請你們斧正。

相關文章
相關標籤/搜索