首頁 > 軟體

 python中的元類metaclass詳情

2022-05-30 22:01:53

動機

python語言因為工作偏向於 AI ,所以對於這門語言還停留在表面,對於 python 深層並沒有接觸到。

今天來聊一聊元類(metaclass) ,想必大多數人都或多或少聽過超程式設計這個詞,但是對於超程式設計是什麼以及如何應用超程式設計熟悉應該不多,在 python 中的 metaclass 就是幫助 developer 實現超程式設計,因此產生一個想法

最近時間還算寬裕,所以想要文章認真弄一弄

從一個問題引出 MetaClass

在 python 語言中,並沒有函數過載,我們下面通過一個具體例子來說明。

class A():
    def f(self, x:int):
        print('A.f int overload',self,x)
    def f(self,x:str):
        print('A.f str overload',self,x)
    def f(self,x,y):
        print('A.f two arg overload',self,x,y)
if __name__ == "__main__":
    a = A()
    a.f(1)

當執行上面程式碼我們會得到一個錯誤資訊,範例化 A 類後,呼叫範例的 f 方法,因為在 python 語言中沒有重灌方法,所以 def f(self,x:str) 會覆蓋之前的 def f(self, x:int), 而 def f(self,x,y) 方法會覆蓋於 def f(self,x:str) 方法,所以當通過傳入 1 一個引數,不會呼叫 def f(self,x:int) 而是呼叫 def f(self,x,y) 方法。

TypeError: f() missing 1 required positional argument: 'y'

那麼什麼是正確的姿勢解決這個問題呢? 這裡先不急於給出答案,當我們介紹完 metaclass 後,答案就自然浮出水面。

Metaclass 程式設計

想要了解 Metaclass 也就是元類,meta 在英文中超越的意思,也就是 Metaclass 是高階於 class,用於建立 class 的 class。有的時候我們需要控制類建立過程,通常建立類工作是由 type 完成的,因為 type 直接設計到 c,我們希望在 type 建立類過程插入一些自定義的東西,所以引入了 Metaclass 讓某一個類建立工作不再由 type 來實現,而是由指定 class 來實現

在 python 中,我們可以通過 class 來範例化物件,不過這裡要說在 python 中 class 其實也是物件。既然 class 也是物件,那麼 class 的型別又是什麼呢

class A:
    a = 1
    b = "hello"
    def f(self):
        return 12
def main():
    print(f'{type(2)=}')
    print(f'{type("hello")=}')
    print(f'{type([])=}')
    print(f'{type(A())=}')
if __name__ == "__main__":
    main()

輸出一下 2、hello 、空陣列和 A 類範例的型別,結果發現他們類別分別為 int、str、list 和 A 類別。其實他們也是物件,既然是物件,那麼就會有一個 class 用於建立這個類別。

type(2)=<class 'int'>
type("hello")=<class 'str'>
type([])=<class 'list'>
type(A())=<class '__main__.A'>

接下來我們就看一下這些 class(int,str,list) 那麼這些物件又是什麼類別呢

class A:
    a = 1
    b = "hello"
    def f(self):
        return 12
if __name__ == "__main__":
    print(f'{type(int)=}')
    print(f'{type(str)=}')
    print(f'{type(list)=}')
    print(f'{type(A)=}')
type(int)=<class 'type'>
type(str)=<class 'type'>
type(list)=<class 'type'>
type(A)=<class 'type'>

不難看出多有 class 的型別都是 type ,例如數位 2 的 int 的一個範例,而 int 又是 type 的一個範例。

如果大家從類似 java 這些語言開始,然後再去學習 python 可能會有這樣疑問,在 python 中 type 和 class 有什麼區別,他們不都是型別嗎? 其實答案就是這兩者在 python3 中並沒有區別,可以將他們看做一個東西。

def main():
    x = int()
    print(f'{x=}')
    B = type('B',(),{})
    print(f'{B=}')
if __name__  == "__main__":
    main()

不過如果進一步深入研究,兩種 class 和 type 在字面上,是不同兩樣東西,class 作為關鍵字來定義型別時,是呼叫其構造器來做了一些初始化的工作。

def main():
    x = int()
    print(f'{x=}')
    B = type('B',(),{})
    print(f'{B=}')
if __name__  == "__main__":
    main()

我們可以這樣來定義一個型別

x=0
B=<class '__main__.B'>

可以用 class 方式來定義一個類 A,然後我們在用 type 方式來建立一個類,type 接受 3 個引數分別是類的名稱,這裡接受的字串型別的名稱、以及該類的基礎類別,是組元的形式,接下來是就是一個屬性,屬性是字典形式資料,鍵是屬性名稱,值是屬性值。

class A:
    a = 2
    b = 'hello'

    def f(self):
        return 12

下面我們用 make_A 來建立一個類, 這裡使用 type 來定義一個類

def make_A():
    name = 'A'
    bases = ()
    a = 2
    b = 'hello'

    def f(self):
        return 12
    namespace = {'a':a,'b':b,'f':f}
    A = type(name,bases,namespace)
    return

通過 type 建立類時候需要傳入類名稱 A 然後 base 是一個要建立類 A 的基礎類別,namescpace 是類屬性,是 dict 形式,鍵是屬性名稱,而值是屬性值。

def make_A_more_accurate():
    name = 'A'
    bases = ()
    namespace = type.__prepare__(name,bases)
    body = (
"""
a = 1
b = 'hello'

def f(self):
    return 12
"""
    )
    exec(body,globals(),namespace)
    A = type(name,bases,namespace)
    return A

metaclass 是繼承於 type,那麼 metaclass 的工作也是用於建立 class,我們可以在 metaclass 中做一些自定義的事情,

這裡可能比較難理解是 __prepare__ 上網找到關於 __prepare__ 解釋,暫時說一下自己認識,可能有點淺,感覺就是為類建立了一個區域性的作用域。

namespace = type.__prepare__(name,bases)
print(namespace)

type.__prepare__ 應該是返回一個區域性名稱空間,

exec(body,globals(),namespace)

class Tut:
    ...
tut = Tut()
print(f'{type(tut)=}')
print(f'{type(Tut)=}')

上面例子定義一個類,然後範例化 Tut 類得到物件 tut,接下來分別輸出 tut 和 Tut 型別

type(tut)=<class '__main__.Tut'>
type(Tut)=<class 'type'>

不難看出 tut 是 Tut 的範例,而 Tut 是 type 的物件

class TutMetaClass(type):
    ...
class Tut(metaclass=TutMetaClass):
    ...

然後我們定義一個 TutMetaClass 繼承於 type,然後將 Tut 類的 metaclass 指向 TutMetaClass ,然後 tut 型別為 Tut,而 Tut 型別為 TutMetaClass 型別

type(tut)=<class '__main__.Tut'>
type(Tut)=<class '__main__.TutMetaClass'>

到此這篇關於 python 中的 元類metaclass詳情的文章就介紹到這了,更多相關 python metaclass 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com