Python中一切皆對象, 那麼類本質上也是一個對象python
類既然也是對象, 那麼就應該有另外一個類來實例化獲得它, 實例化獲得類的類就是元類函數
默認情況下, 元類是 type 這個類, 而且全部的類都是由元類實例化獲得的, 包括他本身測試
class Immortal(object): def __init__(self,name,age): self.name = name self.age = age p = Immortal("太白",4555) print(type(p)) # <class '__main__.Immortal'>
全部的對象都是經過 [類名] + ( ) 獲得的, 也叫作實例化, p 對象就是由 Immortal 類實例化獲得的spa
一切皆對象, 那麼 Immortal 應該也是一個類實例化的結果, 因而咱們能夠推導出 元類+( ) ---> Immortal設計
print(type(Immortal)) # <class 'type'> print(type(type)) # <class 'type'>
經過 type 函數咱們發現 Immortal 的類就是 type 類, 並驚奇的發現 type 類的類是自身3d
由此咱們能夠推斷出 : 元類實例化獲得類, 類實例化獲得對象(實例), 而且驗證了全部類都是由type實例化獲得的, 不信你試試code
上面咱們都是使用 class 這個關鍵字來產生類的, class 關鍵字在幫咱們建立類的時候必然調用了 Immortal = type( )
這種方法, 那麼 type 裏的參數應該是什麼呢?對象
在分析 class 工做流程以前咱們先來了解一下 exec 函數做爲儲備知識 : exec 有三個參數繼承
做用 : 能夠將字符串裏內容當作Python語句來執行, 並將期間產生的名字存放於局部名稱空間中文檔
msg = ''' name = "shawn" age = 22 dic = {"sex":"man"} def test(): print("I am shawn") ''' globals_dic = {} # 全局名稱空間 locals_dict = {} # 局部名稱空間 exec(msg,globals_dic,locals_dict) print(locals_dict) # {'name': 'shawn', 'age': 22, 'dic': {'sex': 'man'}, 'test': <function test at 0x000002596D9BC4C8>} print(locals_dict["name"]) # shawn locals_dict["test"]() # I am shawn
原來咱們使用 class 來建立類, 其中必然調用了元類 type, type中須要傳入三個參數, 這三個參數是類的三大組成部分 :
接下來咱們開始動手建立一個類了
💠設置類名 class_name = "Immortal" 💠設置基類們 class_bases = (object,) 💠設置類體(類的名稱空間) class_body = ''' name = "shawn" age = 22 def print_name(self): print(f"I am {self.name}") ''' class_namespace = {} 💠運行"exec"函數 exec(class_body,{},class_namespace) 💠建立類 Immortal = type(class_name,class_bases,class_namespace) 💠實例化 p = Immortal() print(p.name,p.age) # shawn 22 p.print_name() # I am shawn
以上操做就是class關鍵字的工做流程, 至關於下面的效果
class Immortal(object): name = "shawn" age = 22 def print_name(self): print(f"i am {self.name}") p = Immortal()
🔰由此可知 "class" 關鍵字的本質就是
Immortal = type('Immortal',(object,),dic)
既然咱們已經瞭解了 class 類的工做流程, 那咱們就可使用這種原理來自定義本身的元類
首先咱們得了解, 一個類若是沒有聲明本身的元類, 那麼它默認的元類就是 type, 除了使用默認的元類, 咱們還能夠經過繼承 type 類來自定義元類, 指定元類的關鍵字是 : metaclass
class Monster(metaclass=type): # 一個類默認的元類是type, 像左邊這樣 ... class Demon(metaclass=Mytype): # 像這樣, 咱們可使用metaclass關鍵字來指定一個類的元類 ...
值得注意的是 : 若是是元類, 那麼必須繼承 type, 不然就是一個普通的自定義類
class Mytype(type): # 只有繼承了 type 的類才能稱之爲一個元類, 不然就是一個普通的自定義類 ... class Monster(metaclass=Mytype): # 使用 metaclass 關鍵字來指定元類 Mytype def __init__(self,name,age): self.name = name self.age = age #🔰"class Monster(metaclass=Mytype):" = "Monster = Mytype('Monster',(object,),{})"
上面是一個簡單版的自定義元類, 咱們能夠知道的是
class Monster(metaclass=Mytype):
這一句等於Monster = Mytype('Monster',(object,),{})
,看到這是否是清晰了不少呢?
下面咱們在 Mytype 中進行一些初始化類的設置
class Mytype(type): # 只有繼承了 type 的類才能稱之爲一個元類 def __init__(self,class_name,class_bases,class_namespace): print(self) # <class '__main__.Monster'> print(class_name) # Monster print(class_bases) # (<class 'object'>,) print(class_namespace) # {'__module__': '__main__', '__qualname__': 'Monster', '__init__': <function Monster.__init__ at 0x000002A528B4CDC8>} super().__init__(class_name,class_bases,class_namespace) # 使用父類的__init__來完成初始化 class Monster(metaclass=Mytype): # 指定元類 def __init__(self,name,age): self.name = name self.age = age print(type(Monster)) # <class '__main__.Mytype'> (能夠發現是自定義的元類建立出來的)
接下來咱們再對這個元類添加一些對建立類的限制功能
class Mytype(type): def __init__(self,class_name,class_bases,class_namespace): super().__init__(class_name,class_bases,class_namespace) # 設置需求1 if not class_name.istitle(): raise Exception('類名首字母必須大寫') # 設置需求2 doc = class_namespace.get("__doc__") if not doc or len(doc) == 0: raise Exception('註釋文檔不能爲空') class Monster(metaclass=Mytype): ''' 我是Monster的註釋 ''' def __init__(self,name,age): self.name = name self.age = age
上面建立的類首字母大寫了, 也有註釋, 是正常運行的, 接下來咱們來測試一下兩種異常
💠首字母沒有大寫 class monster(metaclass=Mytype): ''' 我是Monster的註釋 ''' def __init__(self,name,age): self.name = name self.age = age #🔰 拋出異常 : Exception: 類名首字母必須大寫 💠沒有註釋信息 class Monster(metaclass=Mytype): def __init__(self,name,age): self.name = name self.age = age #🔰 拋出異常 : Exception: 註釋文檔不能爲空
在進行操做以前咱們先複習一下 __call__ 方法來進行儲備
__call__
的執行class Person: def __call__(self, *args, **kwargs): print(self) print(args) print(kwargs) P1 = Person() P1(1,2,3,4,name="shawn") # 對象 + ( ) # <__main__.Person object at 0x0000019B82C6AC48> # (1, 2, 3, 4) # {'name': 'shawn'}
由上可知, 調用一個對象最早觸發的就是類中的
__call__
方法, 一切皆對象, 那麼調用這個類的時候是否是應該觸發這個類的類的__call__
方法呢😂
下面咱們來簡單驗證一下
class Mytype(type): def __call__(self, *args, **kwargs): print(self) # <class '__main__.Monster'> print(args) # (1, 2, 3, 'a') print(kwargs) # {'name': 'shawn'} return 111111 class Monster(metaclass=Mytype): ... p = Monster(1,2,3,"a",name = "shawn") print(p) # 111111
顯而易見的結論 :
- 調用 Monster 就是在調用 Mytype 中的
__call__
方法- 而後將 Monster 傳給 Mytype 中的 self, 溢出的位置參數傳給
*
,溢出的關鍵字參數傳給**
- 調用 Monster 的返回值就是調用
__call__
的返回值
默認的, 一個類的調用 (p = Monster("name",29)像這種) 會發生三件事 :
__new__
方法建立一個空對象 obj__init__
方法初始化對象 objclass Mytype(type): def __call__(self, *args, **kwargs): print("i am Mytype_call") # 添加測試 obj = self.__new__(self) # 建立空對象 obj self.__init__(obj,*args,**kwargs) # 經過父類來完成obj的初始化 (self=Monster,obj=p) return obj # 返回初始化以後的 obj class Monstar(metaclass=Mytype): def __init__(self,name,age): self.name = name self.age = age p = Monstar("shawn",22) # i am Mytype_call print(p.name) # shawn print(p.age) # 22 print(p) # <__main__.Monstar object at 0x000001881394AD08>
當整個流程已經明瞭以後咱們就能夠爲所欲爲的對調用過程作一些"手腳"
__init__
方法class Mytype(type): def __call__(self, *args, **kwargs): obj = self.__new__(self) for k,v in kwargs.items(): # 咱們直接遍歷 kwargs 取出 key 和 value obj.__dict__[k] = v # 再將其放入到對象的屬性字典中去 return obj class Person(metaclass=Mytype): def print_dict(self): print(self.__dict__) p = Person(name="shawn",age=22) # 這種設計方式就須要你調用傳參的時候按照鍵值對的形式來傳 print(p.name,p.age) # shawn 22 p.print_dict() # {'name': 'shawn', 'age': 22}
class Mytype(type): def __call__(self, *args, **kwargs): obj = self.__new__(self) # 獲得空對象obj (self = Person) self.__init__(obj,*args,**kwargs) # 初始化對象obj (obj = p) # 循環取出對象 p 屬性字典裏面的 key 和 value 進行隱藏屬性操做(這裏使用的是字典推導式) obj.__dict__ = {f"_{self.__name__}__{k}":v for k,v in obj.__dict__.items()} return obj # 返回處理後的對象 class Person(metaclass=Mytype): def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def print_name(self): # 設置一個查看 name 的方法 print(self.__name) p = Person("shawn",22,"man") # print(p.name) # 拋出異常 : "AttributeError" 沒有該屬性 # print(p.age) # 拋出異常 : "AttributeError" 沒有該屬性 p.print_name() # shawn print(p.__dict__) # {'_Person__name': 'shawn', '_Person__age': 22, '_Person__sex': 'man'}
至此, 自定義元類來控制類的建立過程以及自定義元類來控制類的調用 (類實例化)已經介紹完畢了, 小夥伴有沒有學廢呢?
class Mytype(type): n = 555 def __call__(self, *args, **kwargs): obj=self.__new__(self) self.__init__(obj,*args,**kwargs) return obj class Bar(object): # n=333 ... class Foo(Bar): # n=222 ... class Person(Foo,metaclass=Mytype): # n=111 def __init__(self,name,age): self.name=name self.age=age print(Person.n)
就這樣吧!