久久精品国产亚洲高清|精品日韩中文乱码在线|亚洲va中文字幕无码久|伊人久久综合狼伊人久久|亚洲不卡av不卡一区二区|精品久久久久久久蜜臀AV|国产精品19久久久久久不卡|国产男女猛烈视频在线观看麻豆

    1. <style id="76ofp"></style>

      <style id="76ofp"></style>
      <rt id="76ofp"></rt>
      <form id="76ofp"><optgroup id="76ofp"></optgroup></form>
      1. 千鋒教育-做有情懷、有良心、有品質(zhì)的職業(yè)教育機(jī)構(gòu)

        手機(jī)站
        千鋒教育

        千鋒學(xué)習(xí)站 | 隨時(shí)隨地免費(fèi)學(xué)

        千鋒教育

        掃一掃進(jìn)入千鋒手機(jī)站

        領(lǐng)取全套視頻
        千鋒教育

        關(guān)注千鋒學(xué)習(xí)站小程序
        隨時(shí)隨地免費(fèi)學(xué)習(xí)課程

        當(dāng)前位置:首頁(yè)  >  技術(shù)干貨  > Python之metaclass的原理和用法

        Python之metaclass的原理和用法

        來源:千鋒教育
        發(fā)布人:xqq
        時(shí)間: 2023-11-07 13:37:34 1699335454

        metaclass

        metaclass的英文直譯過來就是元類,這既是一個(gè)概念也可以認(rèn)為是Python當(dāng)中的一個(gè)關(guān)鍵字,不管怎么理解,對(duì)它的內(nèi)核含義并沒有什么影響。我們可以不必糾結(jié),就認(rèn)為它是類的類的意思即可。在這個(gè)用法當(dāng)中,支持我們自己定義一個(gè)類,使得它是后面某一個(gè)類的元類。

        之前使用type動(dòng)態(tài)創(chuàng)建類的時(shí)候,我們傳入了類名,和父類的tuple以及屬性的dict。在metaclass用法當(dāng)中,其實(shí)核心相差不大,只是表現(xiàn)形式有所區(qū)別。我們來看一個(gè)例子即可:

        classAddInfo(type):

        def__new__(cls,name,bases,attr):

        attr['info']='addbymetaclass'

        returnsuper().__new__(cls,name,bases,attr)

        classTest(metaclass=AddInfo):

        pass

        在這個(gè)例子當(dāng)中,我們首先創(chuàng)建了一個(gè)類叫做AddInfo,這是我們定義的一個(gè)元類。由于我們希望通過它來實(shí)現(xiàn)元類的功能,所以我們需要它繼承type類。我們?cè)谥暗奈恼庐?dāng)中說過,在Python面向?qū)ο螽?dāng)中,所有的類的根本來源就是type。也就是說Python當(dāng)中的每一個(gè)類都是type的實(shí)例。

        我們?cè)谶@個(gè)類當(dāng)中重載了__new__方法,我們?cè)赺_new__方法當(dāng)中傳入了四個(gè)參數(shù)。眼尖一點(diǎn)的小伙伴一定已經(jīng)看出來了,這個(gè)函數(shù)的四個(gè)參數(shù),正是我們調(diào)用type創(chuàng)建類的時(shí)候傳入的參數(shù)。其實(shí)我們調(diào)用type的方法來創(chuàng)建類的時(shí)候,就是調(diào)用的__new__這個(gè)函數(shù)完成的,這兩種寫法對(duì)應(yīng)的邏輯是完全一樣的。

        我們之后又創(chuàng)建了一個(gè)新的類叫做Test,這個(gè)當(dāng)中沒有任何邏輯,直接pass。但是我們?cè)趧?chuàng)建類的時(shí)候指定了一個(gè)參數(shù)metaclass=AddInfo,這里這個(gè)參數(shù)其實(shí)就是指定的這個(gè)類的元類,也就是指定這個(gè)類的創(chuàng)建邏輯。雖然我們用代碼寫了類的定義,但是在實(shí)際執(zhí)行的時(shí)候,這個(gè)類是以metaclass為元類創(chuàng)建的。

        根據(jù)上面的邏輯,我們可以知道,Test類在創(chuàng)建的時(shí)候就被賦予了類屬性info。我們可以驗(yàn)證一下:

        拓展類功能

        上面這段就是元類的基本用法了,其實(shí)本質(zhì)上和我們之前介紹的type的動(dòng)態(tài)類創(chuàng)建是一樣的,只不過展現(xiàn)的形式不同。那么我們就有一個(gè)問題要問了,我們使用元類究竟能夠做什么呢?

        這里有一個(gè)經(jīng)典的例子,我們都知道Python原生的list是沒有'add'這個(gè)方法的。假設(shè)我們習(xí)慣了Java當(dāng)中l(wèi)ist的使用,習(xí)慣用add來為它添加元素。我們希望創(chuàng)建一個(gè)新的類,在這個(gè)新的類當(dāng)中,我們可以通過add來添加函數(shù)。通過元類可以很方便地使用這一點(diǎn)。

        classListMeta(type):

        def__new__(cls,name,bases,attrs):

        #在類屬性當(dāng)中添加了add函數(shù)

        #通過匿名函數(shù)映射到append函數(shù)上

        attrs['add']=lambdaself,value:self.append(value)

        returnsuper().__new__(cls,name,bases,attrs)

        classMyList(list,metaclass=ListMeta):

        pass

        我們首先是定義了一個(gè)叫做ListMeta的元類,在這個(gè)元類當(dāng)中我們給類添加了一個(gè)屬性叫做add。它只是包裝了一下而已,底層是通過append方法實(shí)現(xiàn)的。我們來實(shí)驗(yàn)一下:

        從結(jié)果來看也沒什么問題,我們成功通過調(diào)用add方法往list當(dāng)中插入了元素。這里藏著一個(gè)小細(xì)節(jié),我們?cè)贚istMeta當(dāng)中為attrs添加了一個(gè)名叫'add'的屬性。這個(gè)屬性是添加給類的,而不是類初始化出來的實(shí)例的。所以如果我們print出MyList這個(gè)類當(dāng)中的所有屬性,也能看到add的存在。

        如果我們直接去通過MyList去訪問add方法的話會(huì)引起報(bào)錯(cuò),因?yàn)槲覀儗?shí)現(xiàn)add這個(gè)方法邏輯的匿名函數(shù)限制了需要傳入兩個(gè)參數(shù)。第一個(gè)參數(shù)是實(shí)例的對(duì)象self,第二個(gè)參數(shù)才是添加的元素value。如果我們通過MyList的類屬性去訪問它的話會(huì)觸發(fā)一個(gè)錯(cuò)誤,因?yàn)槿鄙倭艘粋€(gè)參數(shù)。因?yàn)轭惍?dāng)中的屬性實(shí)例也是可以調(diào)用的,并且Python會(huì)在參數(shù)前面自動(dòng)添加self這個(gè)參數(shù),就剛好滿足了要求。

        搞明白了這些我們只是解決了可能性問題,我們明白了元類可以實(shí)現(xiàn)這樣的操作,但沒有解決我們?yōu)槭裁幢仨氁褂迷惸?就拿剛才的例子來說,我們完全可以繼承l(wèi)ist這個(gè)類,然后在其中再開發(fā)我們想要的方法,為什么一定要使用元類呢?

        就剛才這個(gè)場(chǎng)景來說,的確,我們是找不出任何理由的。完全沒有理由不使用繼承,而非要用元類。但是在有些場(chǎng)景和有些問題當(dāng)中,我們必須要使用元類不可。就是涉及類屬性變更和類創(chuàng)建的時(shí)候,我們來看下面這個(gè)例子。

        控制實(shí)例的創(chuàng)建

        還記得我們上篇文章介紹的工廠設(shè)計(jì)模式的例子嗎?就是我們可以通過參數(shù)來得到不同類的實(shí)例。

        我們創(chuàng)建了三種游戲的類和一個(gè)工廠類,我們重載了工廠類的__new__函數(shù)。使得我們可以根據(jù)實(shí)例化時(shí)傳入的參數(shù)返回不同類型的實(shí)例。

        classLast_of_us:

        defplay(self):

        print('theLastOfUsisreallyfunny')

        classUncharted:

        defplay(self):

        print('theUnchartedisreallyfunny')

        classPSGame:

        defplay(self):

        print('PShasmanygames')

        classGameFactory:

        games={'last_of_us':Last_of_us,'uncharted':Uncharted}

        def__new__(cls,name):

        ifnameincls.games:

        returncls.games[name]()

        else:

        returnPSGame()

        uncharted=GameFactory('uncharted')

        last_of_us=GameFactory('last_of_us')

        假設(shè)這個(gè)需求完成得很好順利上線了,但是運(yùn)行了一段時(shí)間之后我們發(fā)現(xiàn)下游有的時(shí)候?yàn)榱送祽袝?huì)不通過工廠類來創(chuàng)建實(shí)例,而是直接對(duì)需要的類做實(shí)例化。原本這沒有問題,但是現(xiàn)在產(chǎn)品想要在工廠類當(dāng)中加上一些埋點(diǎn),統(tǒng)計(jì)出訪問我們工廠的訪問量。所以我們需要限制這些游戲類不能直接實(shí)例化,必須要通過工廠返回實(shí)例。

        那么這個(gè)功能我們?cè)趺磳?shí)現(xiàn)呢?

        我們分析一下問題就會(huì)發(fā)現(xiàn),這一次不是需要我們?cè)趧?chuàng)建實(shí)例的時(shí)候做動(dòng)態(tài)的添加,而是直接限制一些類不允許直接調(diào)用進(jìn)行創(chuàng)建。限制的方法比較常用的一種就是拋出異常,所以我們希望可以給這些類加上一個(gè)邏輯,實(shí)例化類的時(shí)候傳入一個(gè)參數(shù),表明是否是通過工廠類進(jìn)行的,如果不是,則拋出異常。

        這里,我們需要用到另外一個(gè)默認(rèn)函數(shù),叫做__call__,它是允許將類實(shí)例當(dāng)做函數(shù)調(diào)用。我們通過類名來實(shí)例化,其實(shí)也是一個(gè)調(diào)用邏輯。這個(gè)__call__的邏輯并不難寫,我們隨手就來:

        def__call__(self,*args,**kwargs):

        iflen(args)==0orargs[0]!='factory':

        raiseTypeError("Can'tinstantiatedirectly")

        但問題是這個(gè)__call__函數(shù)并不能直接加在類當(dāng)中,因?yàn)樗膽?yīng)用范圍是實(shí)例,而不是類。而我們希望的是在創(chuàng)建實(shí)例的時(shí)候進(jìn)行限制,而不是對(duì)調(diào)用實(shí)例的時(shí)候進(jìn)行限制,所以這段邏輯只能通過元類實(shí)現(xiàn)。

        我們直接創(chuàng)建類的時(shí)候就會(huì)觸發(fā)異常,因?yàn)椴皇峭ㄟ^工廠創(chuàng)建的。我們這里判斷是否是工廠創(chuàng)建的邏輯簡(jiǎn)化掉了,只是通過一個(gè)簡(jiǎn)單的字符串來進(jìn)行的判斷,實(shí)際上會(huì)用一些更加復(fù)雜的邏輯,這不是本文的重點(diǎn),我們了解即可。

        整體運(yùn)行的邏輯和我們?cè)O(shè)想的一樣,說明這樣實(shí)現(xiàn)是正確的。

        總結(jié)

        我們?nèi)粘i_發(fā)當(dāng)中用到元類的情況非常罕見,一般都是在一些高端開發(fā)的場(chǎng)景當(dāng)中。比如說開發(fā)一些框架或者是中間件,為了方便下游的使用,需要?jiǎng)?chuàng)建一些關(guān)于類屬性的動(dòng)態(tài)邏輯,才會(huì)用到元類。對(duì)于普通開發(fā)者而言,如果你無法理解元類的含義以及應(yīng)用,也沒有關(guān)系,使用頻率非常低。

        另外,元類的概念和動(dòng)態(tài)類、動(dòng)態(tài)語(yǔ)言的概念有關(guān),Python語(yǔ)言的動(dòng)態(tài)特性很多正是通過這一點(diǎn)體現(xiàn)的。所以隨著我們對(duì)于Python動(dòng)態(tài)特性理解的加深,理解元類也會(huì)變得越來越容易,同樣也會(huì)理解越來越深刻。如果我們把Python的元類和裝飾器做一個(gè)類比的話,會(huì)發(fā)現(xiàn)兩者的核心邏輯是很類似的。本質(zhì)上都是在原有的邏輯之外封裝新的邏輯,只不過裝飾器針對(duì)的是一段邏輯,而元類針對(duì)的是類的屬性和創(chuàng)建過程。

        以上內(nèi)容為大家介紹了Python之metaclass的原理和用法,希望對(duì)大家有所幫助,如果想要了解更多Python相關(guān)知識(shí),請(qǐng)關(guān)注IT培訓(xùn)機(jī)構(gòu):千鋒教育。

        聲明:本站稿件版權(quán)均屬千鋒教育所有,未經(jīng)許可不得擅自轉(zhuǎn)載。
        10年以上業(yè)內(nèi)強(qiáng)師集結(jié),手把手帶你蛻變精英
        請(qǐng)您保持通訊暢通,專屬學(xué)習(xí)老師24小時(shí)內(nèi)將與您1V1溝通
        免費(fèi)領(lǐng)取
        今日已有369人領(lǐng)取成功
        劉同學(xué) 138****2860 剛剛成功領(lǐng)取
        王同學(xué) 131****2015 剛剛成功領(lǐng)取
        張同學(xué) 133****4652 剛剛成功領(lǐng)取
        李同學(xué) 135****8607 剛剛成功領(lǐng)取
        楊同學(xué) 132****5667 剛剛成功領(lǐng)取
        岳同學(xué) 134****6652 剛剛成功領(lǐng)取
        梁同學(xué) 157****2950 剛剛成功領(lǐng)取
        劉同學(xué) 189****1015 剛剛成功領(lǐng)取
        張同學(xué) 155****4678 剛剛成功領(lǐng)取
        鄒同學(xué) 139****2907 剛剛成功領(lǐng)取
        董同學(xué) 138****2867 剛剛成功領(lǐng)取
        周同學(xué) 136****3602 剛剛成功領(lǐng)取
        相關(guān)推薦HOT
        python虛擬環(huán)境工具virtualenv

        virtualenv是一個(gè)創(chuàng)建隔絕的Python環(huán)境的工具。virtualenv創(chuàng)建一個(gè)包含所有必要的可執(zhí)行文件的文件夾,用來使用Python工程所需的包。安裝pipins...詳情>>

        2023-11-07 16:30:22
        python函數(shù)調(diào)用和參數(shù)傳遞

        定義過函數(shù)后,就可以在后面程序中使用這一函數(shù)printsquare_sum(3,4)python通過位置,知道3對(duì)應(yīng)的是函數(shù)定義中的第一個(gè)參數(shù)a,4對(duì)應(yīng)第二個(gè)參數(shù)b...詳情>>

        2023-11-07 16:19:34
        python字節(jié)碼和機(jī)器碼的區(qū)別

        機(jī)器碼,學(xué)名機(jī)器語(yǔ)言指令,有時(shí)也被稱為原生碼,是電腦的CPU可直接解讀的數(shù)據(jù)。字節(jié)碼是一種中間狀態(tài)(中間碼)的二進(jìn)制代碼(文件)。需要直譯器...詳情>>

        2023-11-07 16:05:10
        Python常用切片操作

        以列表:a=[0,1,2,3,4,5,6,7,8,9]為說明對(duì)象1.取偶數(shù)位置>>>b=a[::2][0,2,4,6,8]2.取奇數(shù)位置>>>b=a[1::2][1,3,5,7,9]3.拷貝整個(gè)對(duì)象>...詳情>>

        2023-11-07 15:21:58
        Python集合類型

        python目前有兩種內(nèi)置集合類型,set和frozenset。Ⅰ、兩者區(qū)別set是可變的,沒有哈希值,其內(nèi)容可以使用add()和remove()這樣的方法來改變,所以...詳情>>

        2023-11-07 15:18:22
        冷水江市| 台湾省| 临颍县| 鄢陵县| 西吉县| 吉隆县| 辽源市| 抚州市| 广汉市| 昌图县| 哈巴河县| 建瓯市| 营口市| 启东市| 大丰市| 时尚| 黄冈市| 克什克腾旗| 永修县| 晋中市| 安阳县| 上虞市| 会东县| 朔州市| 阿尔山市| 鹤山市| 仁布县| 陇川县| 永靖县| 余庆县| 禹州市| 台安县| 库车县| 宁陕县| 卢湾区| 九龙县| 上栗县| 临沧市| 普格县| 门头沟区| 汤原县|