<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python里最神秘的一個(gè)魔法函數(shù)!

          共 4155字,需瀏覽 9分鐘

           ·

          2021-12-25 22:04



          一個(gè)非常神秘的魔術(shù)方法。

          這個(gè)方法非常不起眼,用途狹窄,我?guī)缀鯊奈醋⒁膺^(guò)它,然而,當(dāng)發(fā)現(xiàn)它可能是上述“定律”的唯一例外情況時(shí),我認(rèn)為值得再寫(xiě)一篇文章來(lái)詳細(xì)審視一下它。

          本文主要關(guān)注的問(wèn)題有:
          (1) __missing__()到底是何方神圣?
          (2) __missing__()有什么特別之處?擅長(zhǎng)“大變活人”魔術(shù)?

          (3) __missing__()是否真的是上述發(fā)現(xiàn)的例外?如果是的話,為什么會(huì)有這種特例?

          1、有點(diǎn)價(jià)值的__missing__()

          從普通的字典中取值時(shí),可能會(huì)出現(xiàn) key 不存在的情況:
          dd?=?{'name':'PythonCat'}
          dd.get('age')????????#?結(jié)果:None
          dd.get('age',?18)????#?結(jié)果:18
          dd['age']????????????#?報(bào)錯(cuò)?KeyError
          dd.__getitem__('age')??#?等同于?dd['age']

          對(duì)于 get() 方法,它是有返回值的,而且可以傳入第二個(gè)參數(shù),作為 key 不存在時(shí)的返回內(nèi)容,因此還可以接受。但是,另外兩種寫(xiě)法都會(huì)報(bào)錯(cuò)。

          為了解決后兩種寫(xiě)法的問(wèn)題,就可以用到 __missing__() 魔術(shù)方法。

          現(xiàn)在,假設(shè)我們有一個(gè)這樣的訴求:從字典中取某個(gè) key 對(duì)應(yīng)的 value,如果有值則返回值,如果沒(méi)有值則插入 key,并且給它一個(gè)默認(rèn)值(例如一個(gè)空列表)。

          如果用原生的 dict,并不太好實(shí)現(xiàn),但是,Python 提供了一個(gè)非常好用的擴(kuò)展類collections.defaultdict

          如圖所示,當(dāng)取不存在的 key 時(shí),沒(méi)有再報(bào) KeyError,而是默認(rèn)存入到字典中。

          為什么 defaultdict 可以做到這一點(diǎn)呢?

          原因是 defaultdict 在繼承了內(nèi)置類型 dict 之后,還定義了一個(gè) __missing__() 方法,當(dāng) __getitem__取不存在的值時(shí),它就會(huì)調(diào)用入?yún)⒅袀魅氲墓S函數(shù)(上例是調(diào)用 list(),創(chuàng)建空列表)。

          作為最典型的示例,defaultdict 在文檔注釋中寫(xiě)到:

          簡(jiǎn)而言之,__missing__()的主要作用就是由__getitem__在缺失 key 時(shí)調(diào)用,從而避免出現(xiàn) KeyError。

          另外一個(gè)典型的使用例子是collections.Counter,它也是 dict 的子類,在取未被統(tǒng)計(jì)的 key 時(shí),返回計(jì)數(shù) 0:

          2、神出鬼沒(méi)的__missing__()

          由上可知,__missing__()在__getitem__()取不到值時(shí)會(huì)被調(diào)用,但是,我不經(jīng)意間還發(fā)現(xiàn)了一個(gè)細(xì)節(jié):__getitem__()在取不到值時(shí),并不一定會(huì)調(diào)用__missing__()。

          這是因?yàn)樗⒎莾?nèi)置類型的必要屬性,并沒(méi)有在字典基類中被預(yù)先定義。

          如果你直接從 dict 類型中取該屬性值,會(huì)報(bào)屬性不存在:AttributeError: type object 'object' has no attribute '__missing__'。

          使用 dir() 查看,發(fā)現(xiàn)確實(shí)不存在該屬性:

          如果從 dict 的父類即 object 中查看,也會(huì)發(fā)現(xiàn)同樣的結(jié)果。

          這是怎么回事呢?為什么在 dict 和 object 中都沒(méi)有__missing__屬性呢?

          然而,查閱最新的官方文檔,object 中分明包含這個(gè)屬性:
          出處:https://docs.python.org/3/reference/datamodel.html?highlight=__missing__#object.__missing__

          也就是說(shuō),理論上 object 類中會(huì)預(yù)定義__missing__,其文檔證明了這一點(diǎn),然而實(shí)際上它并沒(méi)有被定義!文檔與現(xiàn)實(shí)出現(xiàn)了偏差!

          如此一來(lái),當(dāng) dict 的子類(例如 defaultdict 和 Counter)在定義__missing__ 時(shí),這個(gè)魔術(shù)方法事實(shí)上只屬于該子類,也就是說(shuō),它是一個(gè)誕生于子類中的魔術(shù)方法!

          據(jù)此,我有一個(gè)不成熟的猜想:__getitem__()會(huì)判斷當(dāng)前對(duì)象是否是 dict 的子類,且是否擁有__missing__(),然后才會(huì)去調(diào)用它(如果父類中也有該方法,則不會(huì)先作判斷,而是直接就調(diào)用了)。

          我在交流群里說(shuō)出了這個(gè)猜想,有同學(xué)很快在 CPython 源碼中找到驗(yàn)證:

          而這就有意思了,在內(nèi)置類型的子類上才存在的魔術(shù)方法,縱觀整個(gè) Python 世界,恐怕再難以找出第二例。

          我突然有一個(gè)聯(lián)想:這神出鬼沒(méi)的__missing__(),就像是一個(gè)擅長(zhǎng)玩“大變活人”的魔術(shù)師,先讓觀眾在外面透過(guò)玻璃看到他(即官方文檔),然而揭開(kāi)門時(shí),他并不在里面(即內(nèi)置類型),再變換一下道具,他又完好無(wú)損就出現(xiàn)了(即 dict 的子類)。

          3、被施魔法的__missing__()

          __missing__() 的神奇之處,除了它本身會(huì)變“魔術(shù)”之外,它還需要一股強(qiáng)大的“魔法”才能驅(qū)動(dòng)。

          上篇文章中,我發(fā)現(xiàn)原生的魔術(shù)方法間相互獨(dú)立,它們?cè)?C 語(yǔ)言界面可能有相同的核心邏輯,但是在 Python 語(yǔ)言界面,卻并不存在著調(diào)用關(guān)系:

          魔術(shù)方法的這種“老死不相往來(lái)”的表現(xiàn),違背了一般的代碼復(fù)用原則,也是導(dǎo)致內(nèi)置類型的子類會(huì)出現(xiàn)某些奇怪表現(xiàn)的原因。

          官方 Python 寧肯提供新的 UserString、UserList、UserDict 子類,也不愿意復(fù)用魔術(shù)方法,唯一合理的解釋似乎是令魔術(shù)方法相互調(diào)用的代價(jià)太大。

          但是,對(duì)于特例__missing__(),Python 卻不得不妥協(xié),不得不付出這種代價(jià)!

          __missing__() 是魔術(shù)方法的“二等公民”,它沒(méi)有獨(dú)立的調(diào)用入口,只能被動(dòng)地由 __getitem__() 調(diào)用,即__missing__() 依賴于__getitem__()。

          不同于那些“一等公民”,例如 __init__()、__enter__()、__len__()、__eq__() 等等,它們要么是在對(duì)象生命周期或執(zhí)行過(guò)程的某個(gè)節(jié)點(diǎn)被觸發(fā),要么由某個(gè)內(nèi)置函數(shù)或操作符觸發(fā),這些都是相對(duì)獨(dú)立的事件,無(wú)所依賴。

          __missing__() 依賴于__getitem__(),才能實(shí)現(xiàn)方法調(diào)用;而 __getitem__() 也要依賴 __missing__(),才能實(shí)現(xiàn)完整功能。

          為了實(shí)現(xiàn)這一點(diǎn),__getitem__()在解釋器代碼中開(kāi)了個(gè)后門,從 C 語(yǔ)言界面折返回 Python 界面,去調(diào)用那個(gè)名為“__missing__”的特定方法。

          而這就是真正的“魔法”了,目前為止,__missing__()似乎是唯一一個(gè)享受了此等待遇的魔術(shù)方法!

          4、小結(jié)

          Python 的字典提供了兩種取值的內(nèi)置方法,即__getitem__() 和 get(),當(dāng)取值不存在時(shí),它們的處理策略是不一樣的:前者會(huì)報(bào)錯(cuò)KeyError,而后者會(huì)返回 None。

          為什么 Python 要提供兩個(gè)不同的方法呢?或者應(yīng)該問(wèn),為什么 Python 要令這兩個(gè)方法做出不一樣的處理呢?

          這可能有一個(gè)很復(fù)雜(也可能是很簡(jiǎn)單)的解釋,本文暫不深究了。

          不過(guò)有一點(diǎn)是可以確定的:即原生 dict 類型簡(jiǎn)單粗暴地拋KeyError的做法有所不足。

          為了讓字典類型有更強(qiáng)大的表現(xiàn)(或者說(shuō)讓__getitem__()作出 get() 那樣的表現(xiàn)),Python 讓字典的子類可以定義__missing__(),供__getitem__()查找調(diào)用。

          本文梳理了__missing__()的實(shí)現(xiàn)原理,從而揭示出它并非是一個(gè)毫不起眼的存在,恰恰相反,它是唯一一個(gè)打破了魔術(shù)方法間壁壘,支持被其它魔術(shù)方法調(diào)用的特例!

          Python 為了維持魔術(shù)方法的獨(dú)立性,不惜煞費(fèi)苦心地引入了 UserString、UserList、UserDict 這些派生類,但是對(duì)于 __missing__(),它卻選擇了妥協(xié)。

          本文揭示出了這個(gè)魔術(shù)方法的神秘之處,不知你讀后有何感想呢?歡迎留言討論。

          我們的文章到此就結(jié)束啦,如果你喜歡今天的Python 實(shí)戰(zhàn)教程,請(qǐng)持續(xù)關(guān)注我們!



          推薦閱讀:

          入門:?最全的零基礎(chǔ)學(xué)Python的問(wèn)題? |?零基礎(chǔ)學(xué)了8個(gè)月的Python??|?實(shí)戰(zhàn)項(xiàng)目?|學(xué)Python就是這條捷徑


          干貨:爬取豆瓣短評(píng),電影《后來(lái)的我們》?|?38年NBA最佳球員分析?|? ?從萬(wàn)眾期待到口碑撲街!唐探3令人失望? |?笑看新倚天屠龍記?|?燈謎答題王?|用Python做個(gè)海量小姐姐素描圖?|碟中諜這么火,我用機(jī)器學(xué)習(xí)做個(gè)迷你推薦系統(tǒng)電影


          趣味:彈球游戲? |?九宮格? |?漂亮的花?|?兩百行Python《天天酷跑》游戲!


          AI:?會(huì)做詩(shī)的機(jī)器人?|?給圖片上色?|?預(yù)測(cè)收入?|?碟中諜這么火,我用機(jī)器學(xué)習(xí)做個(gè)迷你推薦系統(tǒng)電影


          小工具:?Pdf轉(zhuǎn)Word,輕松搞定表格和水?。?/a>?|?一鍵把html網(wǎng)頁(yè)保存為pdf!|??再見(jiàn)PDF提取收費(fèi)!?|?用90行代碼打造最強(qiáng)PDF轉(zhuǎn)換器,word、PPT、excel、markdown、html一鍵轉(zhuǎn)換?|?制作一款釘釘?shù)蛢r(jià)機(jī)票提示器!?|60行代碼做了一個(gè)語(yǔ)音壁紙切換器天天看小姐姐!


          年度爆款文案


          點(diǎn)閱讀原文,看200個(gè)Python案例!

          瀏覽 57
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美一区视频 | 欧美在线三级视频 | 欧美精品成人网站在线 | 乱伦黄色小说网站 | 一区二区无人区 |