python中metaclass的作用是什么

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)python中metaclass的作用是什么,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

創(chuàng)新互聯(lián)是一家專注網(wǎng)站建設(shè)、網(wǎng)絡(luò)營銷策劃、微信平臺小程序開發(fā)、電子商務(wù)建設(shè)、網(wǎng)絡(luò)推廣、移動互聯(lián)開發(fā)、研究、服務(wù)為一體的技術(shù)型公司。公司成立10多年以來,已經(jīng)為上千三輪攪拌車各業(yè)的企業(yè)公司提供互聯(lián)網(wǎng)服務(wù)?,F(xiàn)在,服務(wù)的上千客戶與我們一路同行,見證我們的成長;未來,我們一起分享成功的喜悅。

類也是對象

在理解metaclass之前,我們先要掌握python中的類(class)是什么。

python中類的概念,是借鑒自smalltalk語言。

在大部分語言中,類指的是"描述如何產(chǎn)生一個(gè)對象(object)"的一段代碼,這對于python也是如此。

>>> class ObjectCreator(object): ...       pass ... >>> my_object = ObjectCreator() >>> print(my_object) <__main__.ObjectCreator object at 0x8974f2c>

但是,在python中,類遠(yuǎn)不止如此,類同時(shí)也是對象。當(dāng)你遇到關(guān)鍵詞class的時(shí)候,python就會自動執(zhí)行產(chǎn)生一個(gè)對象。下面的代碼段中:

>>> class ObjectCreator(object): ...       pass ...

python在內(nèi)存中產(chǎn)生了一個(gè)名叫做"ObjectCreator"的對象。這個(gè)對象(類)自身擁有產(chǎn)生對象(實(shí)例instance)的能力。  這就是為什么稱呼這東西(后面遇到容易混淆的地方,我們稱之為:類對象)也是類的原因。同時(shí),它也是一個(gè)對象,因此你可以對它做如下操作:

  • 賦值給變量

  • 復(fù)制它

  • 為它增加屬性(attribute)

  • 作為參數(shù)傳值給函數(shù)

舉例:

>>> print(ObjectCreator) # 你可以打印一個(gè)類,因?yàn)樗瑫r(shí)也是對象 <class '__main__.ObjectCreator'>  >>> def echo(o): ...     print(o) ... >>> echo(ObjectCreator) # 作為參數(shù)傳值給函數(shù) <class '__main__.ObjectCreator'>  >>> print(hasattr(ObjectCreator, 'new_attribute')) False >>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class >>> print(hasattr(ObjectCreator, 'new_attribute')) True >>> print(ObjectCreator.new_attribute) foo  >>> ObjectCreatorMirror = ObjectCreator # 將類賦值給變量 >>> print(ObjectCreatorMirror.new_attribute) foo >>> print(ObjectCreatorMirror()) <__main__.ObjectCreator object at 0x8997b4c>

動態(tài)創(chuàng)建類

既然類也是對象,那么我們就可以在運(yùn)行的時(shí)候創(chuàng)建它,跟創(chuàng)建對象一樣自然。

首先,我們使用class關(guān)鍵字定義一個(gè)產(chǎn)生類的函數(shù):

>>> def choose_class(name): ...     if name == 'foo': ...         class Foo(object): ...             pass ...         return Foo # return the class, not an instance ...     else: ...         class Bar(object): ...             pass ...         return Bar ... >>> MyClass = choose_class('foo') >>> print(MyClass) # the function returns a class, not an instance <class '__main__.Foo'> >>> print(MyClass()) # you can create an object from this class <__main__.Foo object at 0x89c6d4c>

這很容易理解吧。但是,這并不那么動態(tài)啊。我們還是需要自己來寫這個(gè)類的代碼。

既然類也是對象,那就應(yīng)該有用來產(chǎn)生它的東西。這東西就是type。

先來說說你所認(rèn)識的type。這個(gè)古老而好用的函數(shù),可以讓我們知道一個(gè)對象的類型是什么。

>>> print(type(1)) <type 'int'> >>> print(type("1")) <type 'str'> >>> print(type(ObjectCreator)) <type 'type'> >>> print(type(ObjectCreator())) <class '__main__.ObjectCreator'>

實(shí)際上,type還有一個(gè)完全不同的功能,它可以在運(yùn)行時(shí)產(chǎn)生類。type可以傳入一些參數(shù),然后返回一個(gè)類。(好吧,必須承認(rèn),根據(jù)不同的傳入?yún)?shù),一個(gè)相同的函數(shù)type居然會有兩個(gè)完全不同的作用,這很愚蠢。不過python這樣做是為了保持向后兼容性。)

下面舉例type創(chuàng)建類的用法。首先,對于類一般是這么定義的:

>>> class MyShinyClass(object): ...       pass

在下面,MyShinyClass也可以這樣子被創(chuàng)建出來,并且跟上面的創(chuàng)建方法有一樣的表現(xiàn):

>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object >>> print(MyShinyClass) <class '__main__.MyShinyClass'> >>> print(MyShinyClass()) # create an instance with the class <__main__.MyShinyClass object at 0x8997cec>

type創(chuàng)建類需要傳入三個(gè)參數(shù),分別為:

  • 類的名字

  • 一組"類的父類"的元組(tuple) (這個(gè)會實(shí)現(xiàn)繼承,也可以為空)

  • 字典 (類的屬性名與值,key-value的形式,不傳相當(dāng)于為空,如一般寫法中的pass).

下面來點(diǎn)復(fù)雜的,來更好的理解type傳入的三個(gè)參數(shù):

class Foo(object):     bar = True      def echo_bar(self):         print(self.bar)

等價(jià)于:

def echo_bar(self):     print(self.bar)  Foo = type('Foo', (), {'bar':True, 'echo_bar': echo_bar})

想要看點(diǎn)有繼承關(guān)系的類的實(shí)現(xiàn),來:

class FooChild(Foo):     pass

等價(jià)于:

FooChild = type('FooChild', (Foo, ), {})

回顧一下我們學(xué)到哪了: 在python中,類就是對象,并且你可以在運(yùn)行的時(shí)候動態(tài)創(chuàng)建類.

那到底什么是metaclass(元類)

metaclass 就是創(chuàng)建類的那家伙。(事實(shí)上,type就是一個(gè)metaclass)

我們知道,我們定義了class就是為了能夠創(chuàng)建object的,沒錯(cuò)吧?

我們也學(xué)習(xí)了,python中類也是對象。

那么,metaclass就是用來創(chuàng)造“類對象”的類.它是“類對象”的“類”。

可以這樣子來理解:

python中metaclass的作用是什么

MyClass = MetaClass() MyObject = MyClass()

也可以用我們上面學(xué)到的type來表示:

MyClass = type('MyClass', (), {})

說白了,函數(shù)type就是一個(gè)特殊的metaclass.

python在背后使用type創(chuàng)造了所有的類。type是所有類的metaclass.

我們可以使用__class__屬性來驗(yàn)證這個(gè)說法.

在python中,一切皆為對象:整數(shù)、字符串、函數(shù)、類.所有這些對象,都是通過類來創(chuàng)造的.

>>> age = 35 >>> age.__class__ <type 'int'>  >>> name = 'bob' >>> name.__class__ <type 'str'>  >>> def foo(): pass >>> foo.__class__ <type 'function'>  >>> class Bar(object): pass >>> b = Bar() >>> b.__class__ <class '__main__.Bar'>

那么,__class__的__class__又是什么呢?

>>> age.__class__.__class__ <type 'type'> >>> name.__class__.__class__ <type 'type'> >>> foo.__class__.__class__ <type 'type'> >>> b.__class__.__class__ <type 'type'>

metaclass就是創(chuàng)造類對象的工具.如果你喜歡,你也可以稱之為"類的工廠".

type是python內(nèi)置的metaclass。不過,你也可以編寫自己的metaclass.

__metaclass__ 屬性

我們可以在一個(gè)類中加入 __metaclass__ 屬性.

class Foo(object):     __metaclass__ = something...     [...]

當(dāng)你這么做了,python就會使用metaclass來創(chuàng)造類:Foo。

注意啦,這里有些技巧的。

當(dāng)你寫下class Foo(object)的時(shí)候,類對象Foo還沒有在內(nèi)存中生成。

python會在類定義中尋找__metaclass__ 。如果找到了,python就會使用這個(gè)__metaclass__ 來創(chuàng)造類對象:  Foo。如果沒找到,python就使用type來創(chuàng)造Foo。

請把下面的幾段話重復(fù)幾遍:

當(dāng)你寫如下代碼的時(shí)候:

class Foo(Bar):  pass

python做了以下事情:

Foo中有__metaclass__這個(gè)屬性嗎?

如果有,python會在內(nèi)存中通過__metaclass__創(chuàng)建一個(gè)名字為Foo的類對象。

如果python沒有在Foo中找到__metaclass__,它會繼續(xù)在Bar(父類)中尋找__metaclass__,并嘗試做和前面同樣的操作。

如果python由下往上遍歷父類也都沒有找不到__metaclass__,它就會在模塊(module)中去尋找__metaclass__,并嘗試做同樣的操作。

如果還是沒有找不到__metaclass__, python才會用內(nèi)置的type(這也是一個(gè)metaclass)來創(chuàng)建這個(gè)類對象。

現(xiàn)在問題來了,我們要怎么用代碼來實(shí)現(xiàn)__metaclass__呢? 寫一些可以用來產(chǎn)生類(class)的東西就行。

那什么可以產(chǎn)生類?無疑就是type,或者type的任何子類,或者任何使用到type的東西都行.

自定義metaclass

使用metaclass的主要目的,是為了能夠在創(chuàng)建類的時(shí)候,自動地修改類。

一個(gè)很傻的需求,我們決定要將該模塊中的所有類的屬性,改為大寫。

有幾種方法可以做到,這里使用__metaclass__來實(shí)現(xiàn).

在模塊的層次定義metaclass,模塊中的所有類都會使用它來創(chuàng)造類。我們只需要告訴metaclass,將所有的屬性轉(zhuǎn)化為大寫。

# type也是一個(gè)類,我們可以繼承它. class UpperAttrMetaclass(type):     # __new__ 是在__init__之前被調(diào)用的特殊方法     # __new__是用來創(chuàng)建對象并返回這個(gè)對象     # 而__init__只是將傳入的參數(shù)初始化給對象     # 實(shí)際中,你很少會用到__new__,除非你希望能夠控制對象的創(chuàng)建     # 在這里,類是我們要?jiǎng)?chuàng)建的對象,我們希望能夠自定義它,所以我們改寫了__new__     # 如果你希望的話,你也可以在__init__中做些事情     # 還有一些高級的用法會涉及到改寫__call__,但這里我們就先不這樣.      def __new__(upperattr_metaclass, future_class_name,                 future_class_parents, future_class_attr):          uppercase_attr = {}         for name, val in future_class_attr.items():             if not name.startswith('__'):                 uppercase_attr[name.upper()] = val             else:                 uppercase_attr[name] = val         return type(future_class_name, future_class_parents, uppercase_attr)

這里的方式其實(shí)不是OOP(面向?qū)ο缶幊?.因?yàn)槲覀冎苯诱{(diào)用了type,而不是改寫父類的__type__方法.

所以我們也可以這樣子處理:

class UpperAttrMetaclass(type):      def __new__(upperattr_metaclass, future_class_name,                 future_class_parents, future_class_attr):          uppercase_attr = {}         for name, val in future_class_attr.items():             if not name.startswith('__'):                 uppercase_attr[name.upper()] = val             else:                 uppercase_attr[name] = val         return type.__new__(upperattr_metaclass, future_class_name,                             future_class_parents, uppercase_attr)

這樣子看,我們只是復(fù)用了 type.__new__方法,這就是我們熟悉的基本的OOP編程,沒什么魔法可言.

你可能注意到,__new__方法相比于

type(future_class_name, future_class_parents, future_class_attr)

多了一個(gè)參數(shù): upperattr_metaclass, 請別在意,這沒什么特別的: __new__總是將"它要定義的類"作為***個(gè)參數(shù)。

這就好比是 self 在類的一般方法(method)中一樣,也是被作為***個(gè)參數(shù)傳入。

當(dāng)然啦,這里的名字的確是我起的太長了。就像self一樣,所有的參數(shù)都有它們傳統(tǒng)的名稱。因此,在實(shí)際的代碼中,一個(gè)metaclass應(yīng)該是寫成下面樣子的:

(我們同時(shí)使用常見的super來讓代碼更清晰)

class UpperAttrMetaclass(type):      def __new__(cls, clsname, bases, attrs):         uppercase_attr = {}         for name, val in attrs.items():             if not name.startswith('__'):                 uppercase_attr[name.upper()] = val             else:                 uppercase_attr[name] = val         return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, attrs)

使用了 metaclass 的代碼是比較復(fù)雜,但我們使用它的原因并不是為了復(fù)雜, 而是因?yàn)槲覀兺ǔ褂?metaclass 去做一些晦澀的事情,比如,  依賴于自省,控制繼承等等。

確實(shí),用 metaclass 來搞些“黑魔法”是特別有用的,因而會復(fù)雜化代碼。

但就metaclass本身而言,它們其實(shí)是很簡單的:中斷類的默認(rèn)創(chuàng)建、修改類、***返回修改后的類.

到底為什么要使用metaclass

現(xiàn)在我們面臨一個(gè)問題: 為什么要使用metaclass? 它容易出錯(cuò)且晦澀難懂.

好吧,一般來說,我們根本就用不上它, 99%的用戶應(yīng)該根本不必為此操心。

實(shí)際用到metaclass的人,很清楚他們到底需要做什么,根本不用解釋為什么要用.

metaclass 的一個(gè)主要用途就是構(gòu)建API。Django(一個(gè)python實(shí)現(xiàn)的web框架)的ORM 就是一個(gè)例子。

用Django先定義了以下Model:

class Person(models.Model):     name = models.CharField(max_length=30)     age = models.IntegerField()

然后執(zhí)行下面代碼:

guy = Person.objects.get(name='bob')  print guy.age # result is 35

這里打印的輸出并不是IntegerField,而是一個(gè)int,int是從數(shù)據(jù)庫中獲取的.

這是因?yàn)?models.Model 使用  __metaclass__來實(shí)現(xiàn)了復(fù)雜的數(shù)據(jù)庫查詢。但對于你看來,這就是簡單的API而已,不用關(guān)心背后的復(fù)雜工作。

上述就是小編為大家分享的python中metaclass的作用是什么了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

網(wǎng)站標(biāo)題:python中metaclass的作用是什么
轉(zhuǎn)載來源:http://muchs.cn/article2/jchdic.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站導(dǎo)航、商城網(wǎng)站、網(wǎng)站設(shè)計(jì)、做網(wǎng)站、企業(yè)網(wǎng)站制作、網(wǎng)站內(nèi)鏈

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司