Python最會變魔術的魔術方法,我覺得是它!

2020-12-25 小老鼠Python

作者:豌豆花下貓

來源:Python貓

我有一個核心的發現:Python 內置類型的特殊方法(含魔術方法與其它方法)由 C 語言獨立實現,在 Python 層面不存在調用關係。

但是,文中也提到了一個例外:一個非常神秘的魔術方法。

這個方法非常不起眼,用途狹窄,我幾乎從未注意過它,然而,當發現它可能是上述「定律」的唯一例外情況時,我認為值得再寫一篇文章來詳細審視一下它。

本文主要關注的問題有:

(1) __missing__()到底是何方神聖?

(2) __missing__()有什麼特別之處?擅長「大變活人」魔術?

(3) __missing__()是否真的是上述發現的例外?如果是的話,為什麼會有這種特例?

1、有點價值的__missing__()

從普通的字典中取值時,可能會出現 key 不存在的情況:

dd = {'name':'PythonCat'}dd.get('age') # 結果:Nonedd.get('age', 18) # 結果:18dd['age'] # 報錯 KeyErrordd.__getitem__('age') # 等同於 dd['age']

對於 get() 方法,它是有返回值的,而且可以傳入第二個參數,作為 key 不存在時的返回內容,因此還可以接受。但是,另外兩種寫法都會報錯。

為了解決後兩種寫法的問題,就可以用到 __missing__() 魔術方法。

現在,假設我們有一個這樣的訴求:從字典中取某個 key 對應的 value,如果有值則返回值,如果沒有值則插入 key,並且給它一個默認值(例如一個空列表)。

如果用原生的 dict,並不太好實現,但是,Python 提供了一個非常好用的擴展類

collections.defaultdict

如圖所示,當取不存在的 key 時,沒有再報 KeyError,而是默認存入到字典中。

為什麼 defaultdict 可以做到這一點呢?

原因是 defaultdict 在繼承了內置類型 dict 之後,還定義了一個 __missing__() 方法,當 __getitem__取不存在的值時,它就會調用入參中傳入的工廠函數(上例是調用 list(),創建空列表)。

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

簡而言之,__missing__()的主要作用就是由__getitem__在缺失 key 時調用,從而避免出現 KeyError。

另外一個典型的使用例子是

collections.Counter

,它也是 dict 的子類,在取未被統計的 key 時,返回計數 0:

2、神出鬼沒的__missing__()

由上可知,__missing__()在__getitem__()取不到值時會被調用,但是,我不經意間還發現了一個細節:__getitem__()在取不到值時,並不一定會調用__missing__()。

這是因為它並非內置類型的必要屬性,並沒有在字典基類中被預先定義。

如果你直接從 dict 類型中取該屬性值,會報屬性不存在:

AttributeError: type object 'object' has no attribute '__missing__'

使用 dir() 查看,發現確實不存在該屬性:

如果從 dict 的父類即 object 中查看,也會發現同樣的結果。

這是怎麼回事呢?為什麼在 dict 和 object 中都沒有__missing__屬性呢?

然而,查閱最新的官方文檔,object 中分明包含這個屬性:

出處:https://docs.python.org/3/reference/datamodel.html?highlight=__missing__#object.__missing__

也就是說,理論上 object 類中會預定義__missing__,其文檔證明了這一點,然而實際上它並沒有被定義!文檔與現實出現了偏差!

如此一來,當 dict 的子類(例如 defaultdict 和 Counter)在定義__missing__ 時,這個魔術方法事實上只屬於該子類,也就是說,它是一個誕生於子類中的魔術方法!

據此,我有一個不成熟的猜想:__getitem__()會判斷當前對象是否是 dict 的子類,且是否擁有__missing__(),然後才會去調用它(如果父類中也有該方法,則不會先作判斷,而是直接就調用了)。

我在交流群裡說出了這個猜想,有同學很快在 CPython 源碼中找到驗證:

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

我突然有一個聯想:這神出鬼沒的__missing__(),就像是一個擅長玩「大變活人」的魔術師,先讓觀眾在外面透過玻璃看到他(即官方文檔),然而揭開門時,他並不在裡面(即內置類型),再變換一下道具,他又完好無損就出現了(即 dict 的子類)。

3、被施魔法的__missing__()

__missing__() 的神奇之處,除了它本身會變「魔術」之外,它還需要一股強大的「魔法」才能驅動。

在上篇文章中,我發現原生的魔術方法間相互獨立,它們在 C 語言界面可能有相同的核心邏輯,但是在 Python 語言界面,卻並不存在著調用關係:

魔術方法的這種「老死不相往來」的表現,違背了一般的代碼復用原則,也是導致內置類型的子類會出現某些奇怪表現的原因。

官方 Python 寧肯提供新的 UserString、UserList、UserDict 子類,也不願意復用魔術方法,唯一合理的解釋似乎是令魔術方法相互調用的代價太大。

但是,對於特例__missing__(),Python 卻不得不妥協,不得不付出這種代價!

__missing__() 是魔術方法的「二等公民」,它沒有獨立的調用入口,只能被動地由 __getitem__() 調用,即__missing__() 依賴於__getitem__()。

不同於那些「一等公民」,例如 __init__()、__enter__()、__len__()、__eq__() 等等,它們要麼是在對象生命周期或執行過程的某個節點被觸發,要麼由某個內置函數或操作符觸發,這些都是相對獨立的事件,無所依賴。

__missing__() 依賴於__getitem__(),才能實現方法調用;而 __getitem__() 也要依賴 __missing__(),才能實現完整功能。

為了實現這一點,__getitem__()在解釋器代碼中開了個後門,從 C 語言界面折返回 Python 界面,去調用那個名為「__missing__」的特定方法。

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

4、小結

Python 的字典提供了兩種取值的內置方法,即__getitem__() 和 get(),當取值不存在時,它們的處理策略是不一樣的:前者會報錯

KeyError

,而後者會返回 None。

為什麼 Python 要提供兩個不同的方法呢?或者應該問,為什麼 Python 要令這兩個方法做出不一樣的處理呢?

這可能有一個很複雜(也可能是很簡單)的解釋,本文暫不深究了。

不過有一點是可以確定的:即原生 dict 類型簡單粗暴地拋

KeyError

的做法有所不足。

為了讓字典類型有更強大的表現(或者說讓__getitem__()作出 get() 那樣的表現),Python 讓字典的子類可以定義__missing__(),供__getitem__()查找調用。

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

Python 為了維持魔術方法的獨立性,不惜煞費苦心地引入了 UserString、UserList、UserDict 這些派生類,但是對於 __missing__(),它卻選擇了妥協。

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

相關焦點

  • 揭秘真正的魔術鴿,魔術師變魔術用的不是白鴿,那是什麼?
    不論是在電影還是漫畫裡,每當我們看到魔術師將鴿子從帽子裡變出來時,都會感到嘖嘖稱奇。不過你知道嗎?魔術師變魔術使用的其實不是白鴿,而是與白鴿外形相似且經過訓練的白斑鳩。所以千萬不要因為好奇去隨便找一隻白鴿來變魔術,否則百分百穿幫!
  • 劉謙:知道了魔術的秘密,通常會失望
    大家都知道視頻剪輯造假太容易,而春晚魔術之所以萬眾矚目,其實是藉由現場觀眾在做保票,大家默認你首先騙過了現場觀眾,然後電視觀眾才會為神乎其技而鼓掌。當眾換壺的破綻最受傷的不是電視觀眾,而是廣大魔術師同行。這點,我相信劉謙也明白。4日晚上,央視面對面播出了對劉謙的採訪特輯。
  • 西南大學賈老師會變魔術的卓別林
    原標題:西南大學賈老師會變魔術的卓別林 課餘賈兵常給同事和學生們表演魔術,小有人氣。  賈兵正在表演桌子懸浮術  52歲的中年男人賈兵,時不時裝扮成卓別林的樣子站在臺前,變魔術、搞反串,演小丑,說學逗唱,走穴賺錢;不久前,他剛參加了喜劇之王全國電視大賽錄製。但這些本不是他所希望的……  近日,有網友上傳了一則魔術視頻,拍攝於西南大學十教教師休息辦公室,主角正是賈兵,他精彩的表演贏得學生們陣陣喝彩。原來,他的真實身份是西南大學後勤人員。
  • 魔術師大揭秘!那些驚悚的魔術,究竟是怎麼做到的?
    然而,有趣的一點是,當你第一次看這個把戲的時候,你並不會覺得你看不見香菸掉落的位置。正相反,你會覺得你能清清楚楚地看到所有東西。當你看第二遍的時候,你注視著那支香菸掉下來,它仿佛是在嘲笑你的理智,而你會無法相信自己到底是怎麼在第一遍的時候把這個看漏了的。
  • 魔術的電影,電影的魔術
    卓別林的身體重心哪怕是再稍微往後一點,我們就要損失掉有史以來最偉大的喜劇電影演員了……嗎?好吧,你應該也猜到了,這裡卓別林其實一點風險都沒有冒。此處對於「視覺錯位」原理的運用,真的是令人拍案叫絕!你可以把它看作電影裡的魔術效果,比如下面這樣的:觀看巴斯特·基頓的電影,你會驚訝於裡面宛如魔術般鏡頭的數量和質量。或許,我們應該說,優秀的藝術總是共通的。在他這裡,其實已經不分電影或者魔術了。所有的一切都是他講故事的手段。
  • 《致命魔術》:前20年見過的最震撼的魔術
    這是這個中國魔術師對魔術的完全奉獻與自我犧牲。而他的精彩表演也獲得了波登的尊敬。波登堅信,犧牲是揚名立萬的唯一方法,也奠定了他要為魔術事業犧牲一切的決心與堅定。,不惜為魔術而獻身,為了秘密性和魔術完整性的留存,兩人共享一個人的魔術世界和日常生活,甘願變成「同一個人」。
  • 劉謙離開董卿不會變魔術?魔術託升級,換成當紅流量明星
    然而,這個魔術很快就被人破解。有助理託在桌子下面負責遞水壺的視頻流露出來。此低等的破解方法得到劉謙的澄清,劉謙重新錄製了更厲害的魔壺,分享在社交平臺上。這一次他的魔壺,變出的飲料更多樣。劉謙變魔術一直需要一個人來配合,這個人就是董卿。董卿不在的時候,劉謙該怎麼辦呢?網友都在流傳:沒有董卿,劉謙就不會魔術了。為此,節目組請劉謙上吐槽大會,澄清此事。
  • 劉謙變過哪些超級厲害的魔術?
    謝邀 這個問題很有意思,要說劉謙變過的超級厲害的魔術,那可太多了。但是呢,有很多的魔術被稱之為「經典魔術」,這些魔術全世界很多的魔術師都會去表演它。
  • 魔術是不是最酷的撩妹技能?我就想學會這招變巧克力……
    這可不是一個普通的視頻哦,因為它被我們的Yif施加魔法了。一開始他手中是一盒原味的口香糖但是Yif老師並不滿足於各種口味的口香糖,他說他最愛吃的還是巧克力。會變魔術的人真是任性哦在這場春晚首秀中,Yif在短短的時間內變了一連串魔術,可謂是給觀眾過了把眼癮。
  • 《致命魔術》:這是一部關於魔術的電影,是對於生存與死亡的詮釋
    《致命魔術》故事講Jackman飾演的Angier要為妻報仇,Bale飾演的Borden誓要成為頂級魔術師,同時對抗Angier不依不饒的打擊。雙方在同一城市表演號稱魔術最高境界的「大變活人」,而「大變活人」的關鍵就在於它的prestige——如何把已經消失的人再變回來。
  • 揭秘劉謙魔術!進來就學最浪漫的紙牌魔術「法式舌吻」,撩妹一絕
    出來招生橘知道大家一定特別想搞清楚劉謙這個魔術是怎麼變出來的,所以火速去各大平臺尋找揭秘,只是沒想到……居然會在某橙色軟體找到答案橘要誇一句各位商家的商業嗅覺,如果大家沒事想給男/女朋友變個魔術快樂一下,橘推薦一個更合適的。
  • 電影賞析《致命魔術》:致命的魔術,致命的誘惑
    所以波登雖然有歉疚,但並不覺得自己要承擔全部的責任。而波登在發生意外之後,一半是出於逃避責任,一半是自己心懷愧疚,自始自終不願意承認自己打的是什麼結,這讓安傑抱有怨恨。電影的開頭和結尾呼應著浮現老機關師Cutter對prestige內涵的深刻闡述:每一場魔術表演包括三個步驟,第一步叫做pledge,魔術師示一些平常的物品,一副牌、一隻鳥或一個人,並請觀眾檢驗,當然其中一定有假;第二步稱為turn,魔術師利用這平常的物品做出令人嘆為觀止的表演,但你想找出秘訣卻找不到,因為你並沒有真正在看,你也不是真的想獲知真相,你想被欺騙;但僅僅將東西變消失不足以使觀眾鼓掌
  • 春晚光影魔術觀察|魔術的背離與回歸
    但央視春晚對魔術的要求可不僅於此,它所希望的魔術是前所未有、專屬創作且具有話題效應的,於是就有了後面蔡威澤和周杰倫合作的魔術與歌曲《告白氣球》。魔術與歌曲《告白氣球》蔡威澤是出生於中國臺灣現居加拿大的年輕魔術師,2017年因在美國達人秀中的精彩表演一舉成名。今年春晚上他沒有表演擅長的近景魔術,而是做了一個運用新科技手段的光影魔術。
  • 變魔術日記300字(精選四篇)
    變魔術日記300字(一)  今天,吳老師笑眯眯地走進教室,神秘地說:「現在我們來變魔術吧!」
  • 美國達人秀蔡威澤硬幣變玫瑰花魔術揭秘!
    今天要揭秘的魔術,來自於蔡威澤在美國達人秀上的表演。該魔術可以做到在無遮擋的情況下隨心所欲的控制桌子上的四個硬幣。讓硬幣消失並出現,甚至可以將硬幣變成玫瑰花,非常神奇!那麼這個魔術是怎麼變的呢?首先,這個魔術號稱不管你再怎麼放慢都看不出任何破綻。
  • 乾貨 | 只用一個硬幣變的魔術
    一在我初學魔術的那個年代,網上能搜集到或看到的魔術教學數量有限,而當我想要學習硬幣魔術時,我看到的第一張盜版碟叫做《魔幻大硬幣》。沒錯,作者就是劉謙老師在魔術的19種效果裡,最能直接刺激觀眾感官的無疑是消失,出現這2種效果,這2種魔術也大量出現在硬幣流程裡。
  • 我給妹子變了個魔術,她就說要和我一起睡
    我今天也剛看完這部電影,借本文說說自己的感想,下面的內容涉及劇透,不想被透的可以先看完電影再看文章。▼Sleight在國內被翻譯為《魔術技法》,這也預示著整部電影都與魔術有關。電影講述了一個黑人少年Bo,母親意外去世,他和妹妹Tina相依為命。為了養活自己和妹妹,Bo依靠兩種方法掙錢,一種是在街頭表演魔術賣藝,一種是倒賣毒品。
  • 劉謙為毛曉彤變魔術 劉謙假魚變真魚魔術是什麼樣的?
    8月21日,魔術師劉謙在微博發布了一段給毛曉彤表示魔術的視頻,並發文稱:「之前聽說毛曉彤要來做客,所以特意準備了這份鵝外驚喜…」劉謙現場零距離為毛曉彤變魔術,紙上畫魚變真魚,驚喜到了毛曉彤劉謙咬著紙牌,然後吃到嘴裡咀嚼一下,再出吐了出來,紙牌就變了!打開劉謙嘴裡的紙牌,上面是剛剛毛曉彤籤名的紙牌!不僅如此,劉謙還讓毛曉彤在白紙上畫出上金魚,他也畫了一個,白紙上一共兩隻金魚,而且還塗上了紅色。劉謙還拿給大家看他們畫的金魚。之後,劉謙拿這張畫了金魚的白紙在蠟燭上面烤一下,看到紙上都被烤黑了,劉謙覺得可以了!毛曉彤近距離眼看著劉謙變魔術,太神奇了,都驚喜到她了!
  • 我給你變個魔術吧
    魔術,也就是我們以前稱的戲法,是一樣廣受歡迎的娛樂活動。早在西周時期,我國就有了關於魔術的記載,從「吐雲噴火」「口中生人」到「魚龍漫衍」,一個又一個精彩絕倫的魔術,為人們帶來了歡樂。而魔術師作為魔術的創造者與表演者,也是一份較為「神秘」的職業。於正筆下的魔術師沈牧是什麼樣的呢?
  • 《歌手是誰》李晨浩變魔術遭劉謙秒殺
    原標題:《歌手是誰》李晨浩變魔術遭劉謙秒殺 為了能夠瞞天過海,在熱播網劇《盜墓筆記》中扮演「high少」的李晨浩只得別出心裁,在劉謙老師的主場變起了魔術。   在準備好道具,找到助手譚湘君後,李晨浩開始了他的魔術「這個魔術講得是,有的人有偏財運,但是有的人會漏財」,一邊解釋的李晨浩拿起手中的六個硬幣一個一個地放到譚湘君的手中,並告訴她「當我把六個硬幣放到你的手上的時候,第一時間把它們整個抓起來」,不過迅速抓起來的譚湘君卻發現手中只剩下五個硬幣,引得現場一片驚呼。