python元类_python中的元类 metaclass
python中的元類 metaclass
在python中,類(class)本身也是一個實例對象, 它的類型則是元類, 如果沒有指明, 則自定義類的類型是type. 換言之, 我們所定義的普通類都是type的實例對象, 如果一個類繼承了type, 那么這個類就是元類.
1. 什么是元類
一個類繼承了type,那么這個類就是元類
class A(type):
pass
A就是一個元類,元類能用來做什么呢,應該說,絕大多數時候都用不上元類,如果你想使用元類,請確保你非常理解它
2. 元類的__new__方法
在定義一個類時,指定metaclass,就意味著這個類將有所指定的metaclass來創建
class MyMeta(type):
def __new__(cls, *args, **kwargs):
_class = super().__new__(cls, *args, **kwargs)
print(_class.__name__)
return _class
class Animal(metaclass=MyMeta):
def __init__(self, name):
self.name = name
類MyMeta是元類,在定義Animal這個類時,我指定了它的元類是MyMeta,因此,類Animal將由MyMeta的__new__方法來創建,換一個角度來描述,類Animal是類MyMeta的實例對象。在MyMeta的__new__方法中,我使用print語句輸出了__class的__name__屬性,理論分析告訴我們,這個值應該是Animal, 實際結果也確實是如此。
元類是用來創建普通類(自定義類)的,我們可以利用元類對普通類進行一些限制和要求,比如,我們可以要求所有繼承Animal的類必須擁有run方法
from inspect import isfunction
class MyMeta(type):
def __new__(cls, *args, **kwargs):
_class = super().__new__(cls, *args, **kwargs)
if _class.__name__ != 'Animal':
if not hasattr(_class, 'run') or not isfunction(getattr(_class, 'run')):
raise Exception('類{name}沒有實現run方法'.format(name=_class.__name__))
return _class
class Animal(metaclass=MyMeta):
def __init__(self, name):
self.name = name
class Cat(Animal):
def __init__(self, name):
super().__init__(name)
cat = Cat('加菲貓')
類Cat繼承了Animal,那么它的元類也是MyMeta,在MyMeta的__new__方法里將創建出類Cat,創建以后會檢查類Cat是否有run屬性且該屬性是一個函數,如果不滿足條件則拋出異常。如果類Cat實現了run方法,那么上述代碼將正常執行
class Cat(Animal):
def __init__(self, name):
super().__init__(name)
def run(self):
print('run')
cat = Cat('加菲貓')
cat.run()
我們務必想清楚一點,盡管我們在腳本里使用class定義了類Cat, 但并不是真正的創建了類Cat,我們所寫的代碼僅僅是一個定義,創建的過程使用元類MyMeta來完成的。
3. 元類的__init__方法
元類的__new__負責構建普通類,__init__負責對這個普通類進行初始化
from inspect import isfunction
class MyMeta(type):
def __new__(cls, *args, **kwargs):
_class = super().__new__(cls, *args, **kwargs)
if _class.__name__ != 'Animal':
if not hasattr(_class, 'run') or not isfunction(getattr(_class, 'run')):
raise Exception('類{name}沒有實現run方法'.format(name=_class.__name__))
return _class
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.home = 'earth'
class Animal(metaclass=MyMeta):
def __init__(self, name):
self.name = name
class Cat(Animal):
def __init__(self, name):
super().__init__(name)
def run(self):
print('run')
print(Animal.home)
print(Cat.home)
在元類的__init__方法里,self參數就是我們所創建的類,Animal和Cat, 我們為他們增加了類屬性home, 重載__init__方法,可以更加優雅的實現單例模式
class Singleton(type):
def __init__(self, *args, **kwargs):
self.__instance = None
super().__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
if self.__instance is None:
self.__instance = super().__call__(*args, **kwargs)
return self.__instance
else:
return self.__instance
class FileLock(metaclass=Singleton):
pass
file_lock_1 = FileLock()
file_lock_2 = FileLock()
print(file_lock_1 is file_lock_2)
4. 元類的__call__方法
class MyMeta(type):
def __call__(self, *args, **kwargs):
raise TypeError('不能創建實例')
class FileTool(metaclass=MyMeta):
@staticmethod
def iter_folder(path):
print('遍歷文件夾')
ft = FileTool()
上面的代碼執行會報錯
Traceback (most recent call last):
File "/Users/kwsy/kwsy/coolpython/demo.py", line 13, in
ft = FileTool()
File "/Users/kwsy/kwsy/coolpython/demo.py", line 5, in __call__
raise TypeError('不能創建實例')
TypeError: 不能創建實例
類FileTool是元類MyMeta的一個示例,那么當執行FileTool()時,不正是在調用元類MyMeta的__call__方法么,而MyMeta的__call__方法偏偏拋出一個類型異常,這就導致FileTool不能被實例化,我們只能使用它的靜態方法。
重載元類的__call__方法和類cat的__del__方法可以讓我們控制類的實例化過程,我們可以控制一個類的實例數量
class MyMeta(type):
def __init__(self, *args, **kwargs):
self.instance_count = 0
super().__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
if self.instance_count < 3:
self.instance_count += 1
return type.__call__(self, *args, **kwargs)
else:
raise Exception("類{name}的實例總數超出限制".format(name=self.__name__))
def __del__(self):
self.instance_count -= 1
class Cat(metaclass=MyMeta):
def __init__(self, name):
self.name = name
def __del__(self):
Cat.instance_count -= 1
c1 = Cat('c1')
c2 = Cat('c2')
c3 = Cat('c3')
c4 = Cat('c4')
上面的代碼中,當創c4的時候會拋出異常,因為實例的數量已經達到上限,想要創建c4,必須銷毀一個之前創建的對象實例
c1 = Cat('c1')
c2 = Cat('c2')
c3 = Cat('c3')
del c1
c4 = Cat('c4')
銷毀c1時,類屬性instance_count執行了減一操作,因此可以創建出c4。
5.小結
以上示例代碼,不保證有工程實踐意義,純粹是為了講解元類的功能作用而認為制造出來的,坦率的講,在實際工作中,幾乎用不到元類,但我仍然秉持一個觀點:面試造火箭,工作擰螺絲的意義在于,能造火箭的人必然牛逼,你可以放心的把擰螺絲的工作交給他,至于是否浪費資源,如果你不會造火箭,那么請慎言,這還不是你這個層次所能討論的問題。
總結
以上是生活随笔為你收集整理的python元类_python中的元类 metaclass的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 个人房产抵押借贷的条件
- 下一篇: 低保查哪些人的存款