亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

自定義元類

到現(xiàn)在,我們已經(jīng)知道元類是什么東東了。那么,從始至終我們還不知道元類到底有啥用。只是了解了一下元類。在了解它有啥用的時候,我們先來了解下怎么自定義元類。因為只有了解了怎么自定義才能更好的理解它的作用。

首先我們來了解下 __metaclass__ 屬性

metaclass,直譯為元類,簡單的解釋就是:

當(dāng)我們定義了類以后,就可以根據(jù)這個類創(chuàng)建出實例,所以:先定義類,然后創(chuàng)建實例。

但是如果我們想創(chuàng)建出類呢?那就必須根據(jù)metaclass創(chuàng)建出類,所以:先定義metaclass,然后創(chuàng)建類。

連接起來就是:先定義metaclass,就可以創(chuàng)建類,最后創(chuàng)建實例。

所以,metaclass允許你創(chuàng)建類或者修改類。換句話說,你可以把類看成是metaclass創(chuàng)建出來的“實例”。

class MyObject(object):
    __metaclass__ = something…
[…]

如果是這樣寫的話,Python 就會用元類來創(chuàng)建類 MyObject。當(dāng)你寫下 class MyObject(object),但是類對象 MyObject 還沒有在內(nèi)存中創(chuàng)建。Python 會在類的定義中尋找 __metaclass__ 屬性,如果找到了,Python 就會用它來創(chuàng)建類 MyObject,如果沒有找到,就會用內(nèi)建的 type 函數(shù)來創(chuàng)建這個類。如果還不怎么理解,看下下面的流程圖:

c7dd72cd3c802993425a3b6516a97e2.png

再舉個實例:

class Foo(Bar):
    pass

它的判斷流程是怎樣的呢?

首先判斷 Foo 中是否有 __metaclass__ 這個屬性?如果有,Python 會在內(nèi)存中通過 __metaclass__ 創(chuàng)建一個名字為 Foo 的類對象(注意,這里是類對象)。如果 Python 沒有找到__metaclass__ ,它會繼續(xù)在 Bar(父類)中尋找__metaclass__ 屬性,并嘗試做和前面同樣的操作。如果 Python在任何父類中都找不到 __metaclass__ ,它就會在模塊層次中去尋找 __metaclass__ ,并嘗試做同樣的操作。如果還是找不到 __metaclass__ ,Python 就會用內(nèi)置的 type 來創(chuàng)建這個類對象。

其實 __metaclass__ 就是定義了 class 的行為。類似于 class 定義了 instance 的行為,metaclass 則定義了 class 的行為??梢哉f,class 是 metaclass 的 instance。

現(xiàn)在,我們基本了解了 __metaclass__ 屬性,但是,也沒講過如何使用這個屬性,或者說這個屬性可以放些什么?

答案就是:可以創(chuàng)建一個類的東西。那么什么可以用來創(chuàng)建一個類呢?type,或者任何使用到 type 或者子類化 type 的東東都可以。

元類的主要目的就是為了當(dāng)創(chuàng)建類時能夠自動地改變類。通常,你會為API 做這樣的事情,你希望可以創(chuàng)建符合當(dāng)前上下文的類。假想一個很傻的例子,你決定在你的模塊里所有的類的屬性都應(yīng)該是大寫形式。有好幾種方法可以辦到,但其中一種就是通過在模塊級別設(shè)定__metaclass__ 。采用這種方法,這個模塊中的所有類都會通過這個元類來創(chuàng)建,我們只需要告訴元類把所有的屬性都改成大寫形式就萬事大吉了。

幸運(yùn)的是,__metaclass__ 實際上可以被任意調(diào)用,它并不需要是一個正式的類。所以,我們這里就先以一個簡單的函數(shù)作為例子開始。

# 元類會自動將你通常傳給‘type’的參數(shù)作為自己的參數(shù)傳入
def upper_attr(future_class_name, future_class_parents, future_class_attr):
    '''返回一個類對象,將屬性都轉(zhuǎn)為大寫形式'''
    #  選擇所有不以'__'開頭的屬性
    attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
# 將它們轉(zhuǎn)為大寫形式
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
 
# 通過'type'來做類對象的創(chuàng)建
return type(future_class_name, future_class_parents, uppercase_attr)
 
__metaclass__ = upper_attr  
#  這會作用到這個模塊中的所有類
 
class Foo(object):
    # 我們也可以只在這里定義__metaclass__,這樣就只會作用于這個類中
    bar = 'bip'
print hasattr(Foo, 'bar')
# 輸出: False
print hasattr(Foo, 'BAR')
# 輸出:True
 
f = Foo()
print f.BAR
# 輸出:'bip'
用 class 當(dāng)做元類的做法:
# 請記住,'type'實際上是一個類,就像'str'和'int'一樣
# 所以,你可以從type繼承
class UpperAttrMetaClass(type):
    # __new__ 是在__init__之前被調(diào)用的特殊方法
    # __new__是用來創(chuàng)建對象并返回之的方法
    # 而__init__只是用來將傳入的參數(shù)初始化給對象
    # 你很少用到__new__,除非你希望能夠控制對象的創(chuàng)建
    # 這里,創(chuàng)建的對象是類,我們希望能夠自定義它,所以我們這里改寫__new__
    # 如果你希望的話,你也可以在__init__中做些事情
    # 還有一些高級的用法會涉及到改寫__call__特殊方法,但是我們這里不用
    def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr):
        attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
        uppercase_attr = dict((name.upper(), value) for name, value in attrs)
        return type(future_class_name, future_class_parents, uppercase_attr)

但是,這種方式其實不是 OOP。我們直接調(diào)用了 type,而且我們沒有改寫父類的 __new__ 方法?,F(xiàn)在讓我們這樣去處理:

class UpperAttrMetaclass(type):
    def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr):
        attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
        uppercase_attr = dict((name.upper(), value) for name, value in attrs)
 
        # 復(fù)用type.__new__方法
        # 這就是基本的OOP編程,沒什么魔法
        return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)

你可能已經(jīng)注意到了有個額外的參數(shù) upperattr_metaclass ,這并沒有什么特別的。類方法的第一個參數(shù)總是表示當(dāng)前的實例,就像在普通的類方法中的 self 參數(shù)一樣。當(dāng)然了,為了清晰起見,這里的名字我起的比較長。但是就像 self 一樣,所有的參數(shù)都有它們的傳統(tǒng)名稱。因此,在真實的產(chǎn)品代碼中一個元類應(yīng)該是像這樣的:

class UpperAttrMetaclass(type):
    def __new__(cls, name, bases, dct):
        attrs = ((name, value) for name, value in dct.items() if not name.startswith('__')
        uppercase_attr  = dict((name.upper(), value) for name, value in attrs)
        return type.__new__(cls, name, bases, uppercase_attr)

如果使用 super 方法的話,我們還可以使它變得更清晰一些,這會緩解繼承(是的,你可以擁有元類,從元類繼承,從 type 繼承)

class UpperAttrMetaclass(type):
    def __new__(cls, name, bases, dct):
        attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
        uppercase_attr = dict((name.upper(), value) for name, value in attrs)
        return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)

通常我們都會使用元類去做一些晦澀的事情,依賴于自省,控制繼承等等。確實,用元類來搞些“黑暗魔法”是特別有用的,因而會搞出些復(fù)雜的東西來。但就元類本身而言,它們其實是很簡單的:

攔截類的創(chuàng)建

修改類

返回修改之后的類

繼續(xù)學(xué)習(xí)
||
提交重置代碼