日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

python元类_python中的元类 metaclass

發(fā)布時(shí)間:2023/12/19 python 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python元类_python中的元类 metaclass 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。