python元类_python中的元类 metaclass
python中的元類 metaclass
在python中,類(class)本身也是一個(gè)實(shí)例對(duì)象, 它的類型則是元類, 如果沒有指明, 則自定義類的類型是type. 換言之, 我們所定義的普通類都是type的實(shí)例對(duì)象, 如果一個(gè)類繼承了type, 那么這個(gè)類就是元類.
1. 什么是元類
一個(gè)類繼承了type,那么這個(gè)類就是元類
class A(type):
pass
A就是一個(gè)元類,元類能用來做什么呢,應(yīng)該說,絕大多數(shù)時(shí)候都用不上元類,如果你想使用元類,請(qǐng)確保你非常理解它
2. 元類的__new__方法
在定義一個(gè)類時(shí),指定metaclass,就意味著這個(gè)類將有所指定的metaclass來創(chuàng)建
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這個(gè)類時(shí),我指定了它的元類是MyMeta,因此,類Animal將由MyMeta的__new__方法來創(chuàng)建,換一個(gè)角度來描述,類Animal是類MyMeta的實(shí)例對(duì)象。在MyMeta的__new__方法中,我使用print語句輸出了__class的__name__屬性,理論分析告訴我們,這個(gè)值應(yīng)該是Animal, 實(shí)際結(jié)果也確實(shí)是如此。
元類是用來創(chuàng)建普通類(自定義類)的,我們可以利用元類對(duì)普通類進(jìn)行一些限制和要求,比如,我們可以要求所有繼承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}沒有實(shí)現(xiàn)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__方法里將創(chuàng)建出類Cat,創(chuàng)建以后會(huì)檢查類Cat是否有run屬性且該屬性是一個(gè)函數(shù),如果不滿足條件則拋出異常。如果類Cat實(shí)現(xiàn)了run方法,那么上述代碼將正常執(zhí)行
class Cat(Animal):
def __init__(self, name):
super().__init__(name)
def run(self):
print('run')
cat = Cat('加菲貓')
cat.run()
我們務(wù)必想清楚一點(diǎn),盡管我們?cè)谀_本里使用class定義了類Cat, 但并不是真正的創(chuàng)建了類Cat,我們所寫的代碼僅僅是一個(gè)定義,創(chuàng)建的過程使用元類MyMeta來完成的。
3. 元類的__init__方法
元類的__new__負(fù)責(zé)構(gòu)建普通類,__init__負(fù)責(zé)對(duì)這個(gè)普通類進(jìn)行初始化
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}沒有實(shí)現(xiàn)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參數(shù)就是我們所創(chuàng)建的類,Animal和Cat, 我們?yōu)樗麄冊(cè)黾恿祟悓傩詇ome, 重載__init__方法,可以更加優(yōu)雅的實(shí)現(xiàn)單例模式
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('不能創(chuàng)建實(shí)例')
class FileTool(metaclass=MyMeta):
@staticmethod
def iter_folder(path):
print('遍歷文件夾')
ft = FileTool()
上面的代碼執(zhí)行會(huì)報(bào)錯(cuò)
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('不能創(chuàng)建實(shí)例')
TypeError: 不能創(chuàng)建實(shí)例
類FileTool是元類MyMeta的一個(gè)示例,那么當(dāng)執(zhí)行FileTool()時(shí),不正是在調(diào)用元類MyMeta的__call__方法么,而MyMeta的__call__方法偏偏拋出一個(gè)類型異常,這就導(dǎo)致FileTool不能被實(shí)例化,我們只能使用它的靜態(tài)方法。
重載元類的__call__方法和類cat的__del__方法可以讓我們控制類的實(shí)例化過程,我們可以控制一個(gè)類的實(shí)例數(shù)量
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}的實(shí)例總數(shù)超出限制".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')
上面的代碼中,當(dāng)創(chuàng)c4的時(shí)候會(huì)拋出異常,因?yàn)閷?shí)例的數(shù)量已經(jīng)達(dá)到上限,想要?jiǎng)?chuàng)建c4,必須銷毀一個(gè)之前創(chuàng)建的對(duì)象實(shí)例
c1 = Cat('c1')
c2 = Cat('c2')
c3 = Cat('c3')
del c1
c4 = Cat('c4')
銷毀c1時(shí),類屬性instance_count執(zhí)行了減一操作,因此可以創(chuàng)建出c4。
5.小結(jié)
以上示例代碼,不保證有工程實(shí)踐意義,純粹是為了講解元類的功能作用而認(rèn)為制造出來的,坦率的講,在實(shí)際工作中,幾乎用不到元類,但我仍然秉持一個(gè)觀點(diǎn):面試造火箭,工作擰螺絲的意義在于,能造火箭的人必然牛逼,你可以放心的把擰螺絲的工作交給他,至于是否浪費(fèi)資源,如果你不會(huì)造火箭,那么請(qǐng)慎言,這還不是你這個(gè)層次所能討論的問題。
總結(jié)
以上是生活随笔為你收集整理的python元类_python中的元类 metaclass的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 个人房产抵押借贷的条件
- 下一篇: 低保查哪些人的存款