元類(轉自https://zhuanlan.zhihu.com/p/23887627)


雖然Python本身很難說是面向對象語言,但Python中的數據模型(data model)可以說真的是“純面向對象”。在Python的世界里,一切皆是對象。無論是數值、字符串、序列、字典、函數、模塊、類、實例、文件等等。

元類(metaclass)是Python 2.2中引入的概念,它的作用是定制類的創建行為。這么解釋可能有點難理解,那么這篇文章就通過實例,一步步解釋Python中的元類。



1. Python中一切皆對象,包括類

class Foo: def hello(self): print("hello world!") return foo = Foo() print(type(foo)) # <class '__main__.Foo'> print(type(foo.hello)) # <class 'method'> print(type(Foo)) # <class 'type'> temp = Foo # 賦值給其他變量 Foo.var = 11 # 增加參數 print(Foo) # 作為函數參數 

例子中type(Foo)說明Foo本身也是一個對象,可以將其賦值給其他對象、對其添加屬性、將其作為函數參數傳遞等等。



2. 類的創建過程

在上邊的例子中,類Foo的創建過程中會執行class語句,此時需要首先確定元類(元類定制類的創建行為)。元類的確定過程如下:

 

  • 確定類Foo的父類是否有參數metaclass,如果沒有則:
  • 確定類Foo的父類的父類是否有參數metaclass,如果沒有則:
  • 使用默認元類type(type的用法會在3中講解)。

上邊的例子中,前2項都不符合,則直接使用默認元類type。即上邊的語句相當於:

def hello(self): print("hello world") return Foo = type("Foo", (object,), {"hello": hello}) 

此時可以看出,實際上類Foo是元類type的實例。參見文章的封面圖。



3. 動態創建類

Python中的類可以動態創建,用的就是默認元類type。動態創建類的type函數原型為:

type(object_or_name, bases, dict) 

這里不過多贅述,上個章節中有介紹。舉個比較復雜的動態創建類的例子:

def init(self, name): self.name = name return def hello(self): print("hello %s" % self.name) return Foo = type("Foo", (object,), {"__init__": init, "hello": hello, "cls_var": 10}) foo = Foo("xianhu") print(foo.hello()) print(Foo.cls_var) 

 

4. 自定義元類

再一次說明實例、類和元類之間的關系:

>>> foo.__class__ # <class 'Foo'> >>> Foo.__class__ # <class 'type'> >>> type.__class__ # <class 'type'> 

foo是Foo的實例,Foo是type的實例,type的類又是type。type是默認的元類。那么如何自定義元類呢?(注意,百分之99的工程中不會用到自定義元類,除非你對元類非常理解)

舉個例子,假設我們需要對一個模塊中的所有函數添加作者屬性。首先自定義一個元類,自定義時,需要繼承默認元類type,並重寫其中的__new__方法:

class Author(type): def __new__(mcs, name, bases, dict): # 添加作者屬性 dict["author"] = "xianhu" return super(Author, mcs).__new__(mcs, name, bases, dict) 

對模塊中所有函數的繼承類參數中添加metaclass參數:

class Foo(object, metaclass=Author): pass foo = Foo() print(foo.author) 

注意:Python3中不再有__metaclass__屬性以及模塊級別的__metaclass__屬性。如果一個模塊中函數較多,可以利用正則表達式進行查找替換。

不過在平時的使用中,我發現了模塊級別有個__build_class__函數,將其改為Author,就能達到模塊級別元類的效果。但具體有哪些副作用還不太清楚,慎用!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM