0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀(guān)看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

__missing__()的實(shí)現(xiàn)原理

科技綠洲 ? 來(lái)源:Python實(shí)用寶典 ? 作者:Python實(shí)用寶典 ? 2023-10-30 14:58 ? 次閱讀

上篇文章中,我有一個(gè)核心的發(fā)現(xiàn):Python 內(nèi)置類(lèi)型的特殊方法(含魔術(shù)方法與其它方法)由 C 語(yǔ)言獨(dú)立實(shí)現(xiàn),在 Python 層面不存在調(diào)用關(guān)系。

但是,文中也提到了一個(gè)例外:一個(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à),為什么會(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ò)展類(lèi)collections.defaultdict

圖片

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

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

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

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

圖片

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

另外一個(gè)典型的使用例子是collections.Counter,它也是 dict 的子類(lèi),在取未被統(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)置類(lèi)型的必要屬性,并沒(méi)有在字典基類(lèi)中被預(yù)先定義。

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

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

圖片

如果從 dict 的父類(lèi)即 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 類(lèi)中會(huì)預(yù)定義__missing__,其文檔證明了這一點(diǎn),然而實(shí)際上它并沒(méi)有被定義!文檔與現(xiàn)實(shí)出現(xiàn)了偏差!

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

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

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

圖片

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

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

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)置類(lèi)型的子類(lèi)會(huì)出現(xiàn)某些奇怪表現(xiàn)的原因。

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

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

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

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

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

為了實(shí)現(xiàn)這一點(diǎn), getitem ()在解釋器代碼中開(kāi)了個(gè)后門(mén),從 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)單)的解釋?zhuān)疚臅翰簧罹苛恕?/p>

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

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

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

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

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀(guān)點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4284

    瀏覽量

    62325
  • python
    +關(guān)注

    關(guān)注

    55

    文章

    4768

    瀏覽量

    84376
  • C 語(yǔ)言
    +關(guān)注

    關(guān)注

    0

    文章

    18

    瀏覽量

    14220
  • key
    key
    +關(guān)注

    關(guān)注

    0

    文章

    48

    瀏覽量

    12807
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    接上開(kāi)發(fā)板后顯示EEPROM missing

    接上開(kāi)發(fā)板后,設(shè)備管理其中顯示:68013-EEPROMmissing在下載程序后卻出現(xiàn)了,這樣一種情況:本應(yīng)該在下載程序后,68013-EEPROM missing的消息應(yīng)當(dāng)消失,但是在這里卻沒(méi)有發(fā)生上述現(xiàn)象
    發(fā)表于 11-21 18:22

    keil中警告missing return value

    今天遇到一個(gè)奇怪的問(wèn)題:{:14:} keil編譯出現(xiàn)DHA200A.C(820): warning C290: missing return value警告!此函數(shù)是void型main函數(shù)并無(wú)
    發(fā)表于 08-30 16:33

    仿真時(shí),LED出現(xiàn)missing是怎么回事

    仿真時(shí),LED出現(xiàn)missing是怎么回事
    發(fā)表于 04-17 19:16

    protues仿真出現(xiàn)元器件missing的問(wèn)題

    protues錯(cuò)誤改了之后出現(xiàn)元器件missing的問(wèn)題如何解決
    發(fā)表于 07-15 20:56

    the #endif for this directive is missing 這個(gè)錯(cuò)誤怎么解決???

    Error[Pe037]: the #endif for this directive is missing這個(gè)錯(cuò)誤怎么解決啊?
    發(fā)表于 07-22 18:27

    在設(shè)計(jì)FPGA程序時(shí),誰(shuí)遇到過(guò)如下的警告?。?One or more signals are missing in the process sensitivity list.

    請(qǐng)問(wèn)。在設(shè)計(jì)FPGA程序時(shí),誰(shuí)遇到過(guò)如下的警告?。?One or more signals are missing in the process sensitivity list.
    發(fā)表于 05-17 15:57

    mixed model MCS8051.DLL failed to authorize - Missing or invalid Customer Key.. [U1]這個(gè)啥情況求幫忙

    mixed model MCS8051.DLL failed to authorize - Missing or invalid Customer Key.. [U1]求幫忙
    發(fā)表于 01-05 12:24

    ccs6.1.2安裝提示Error:missing value to go with key是為什么?

    您 好! 官網(wǎng)下載的最新版的離線(xiàn)的ccs6.1,選好安裝路徑點(diǎn)next后提示: Error:missing value to go with key,請(qǐng)問(wèn)這是為什么? 系統(tǒng)環(huán)境為WIN7旗艦版64位;已關(guān)閉殺毒軟件的文件系統(tǒng)實(shí)時(shí)防護(hù),無(wú)法關(guān)閉其所有功能;安裝路徑不包含中文字符。
    發(fā)表于 05-15 09:41

    Keil編譯出現(xiàn)“main.c(16): error C129: missing ';' before 'num'”怎么解決

    Keil編譯時(shí)提示“main.c(16): error C129: missing ‘;’ before ‘num’”折騰了很久才知道是前面的“typedef unsigned char u8
    發(fā)表于 02-16 06:50

    MDK打開(kāi)工程提示Missing Device是什么原因?

    MDK打開(kāi)工程提示Missing Device 這個(gè)是什么原因
    發(fā)表于 08-18 08:28

    E5515C-14 Wireless Communicati

    need to be inspected for 2 missing washers on the DSP fan assembly (detailed pictures are below). If the washers are missing then t
    發(fā)表于 08-21 11:54 ?9次下載

    NTLDR is missing開(kāi)機(jī)故障如何處理

    NTLDR is missing開(kāi)機(jī)故障如何處理 故障現(xiàn)象:開(kāi)機(jī)自檢一切正常,可是在進(jìn)入系統(tǒng),也就是進(jìn)行系統(tǒng)引導(dǎo)的時(shí)候卡住了,顯示
    發(fā)表于 03-10 11:46 ?9261次閱讀

    EFM8SB1芯科單片機(jī)調(diào)試 打開(kāi)NoOverlay.hwconf文件報(bào)錯(cuò)

    loading the device: missing property: cslib.thresholdsettings.activesensordelta missing prop...
    發(fā)表于 11-25 19:36 ?9次下載
    EFM8SB1芯科單片機(jī)調(diào)試 打開(kāi)NoOverlay.hwconf文件報(bào)錯(cuò)

    Keil編譯時(shí)提示“main.c(16): error C129: missing ';' before 'num'”

    Keil編譯時(shí)提示“main.c(16): error C129: missing ‘;’ before ‘num’”折騰了很久才知道是前面的“typedef unsigned char u8
    發(fā)表于 12-17 18:10 ?16次下載
    Keil編譯時(shí)提示“main.c(16): error C129: <b class='flag-5'>missing</b> ';' before 'num'”

    Missing Container Metrics容器指標(biāo)收集工具

    ./oschina_soft/missing-container-metrics.zip
    發(fā)表于 05-12 14:24 ?1次下載
    <b class='flag-5'>Missing</b> Container Metrics容器指標(biāo)收集工具