python中的元類metaclass詳情
動機
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 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 關於Python 多重繼承時metaclass conflict問題解決與原理探究
- Python黑魔法之metaclass詳情
- Python深入淺出分析元類
- python中的type,元類,類,對象用法
- python變量作用域與列表入門詳解