
最近python這么火,大家是不是也都在用啊。對于小編這種小白來說,對于python的一切都很迷茫,又很好奇,這不就看見了這樣一篇文章:當(dāng)python 中混進(jìn)一只薛定諤的貓……為此,小編還專門去問了度娘python和薛定諤的貓有啥關(guān)系。如果你也想知道的話,跟小編一起來看吧。
以下文章來源: Python貓
作者:豌豆花下貓
圖片來源:pexels
Python 是一門強(qiáng)大的動態(tài)語言,那動態(tài)體現(xiàn)在哪里,強(qiáng)大又體現(xiàn)在哪里呢?
除了好的方面,Python 的動態(tài)性是否還藏著一些使用陷阱呢,有沒有辦法識別與避免呢?
沿著它的動態(tài)特性話題,貓哥有幾篇文章依次探及了:動態(tài)修改變量、動態(tài)定義函數(shù)、動態(tài)執(zhí)行代碼等內(nèi)容,然而,當(dāng)混合了變量賦值、動態(tài)賦值、命名空間、作用域、函數(shù)的編譯原理等等內(nèi)容時(shí),問題就可能會變得非常棘手。
因此,這篇文章將前面一些內(nèi)容融匯起來,再做一次延展的討論,希望能夠理清一些使用的細(xì)節(jié),更深入地探索 Python 語言的奧秘。
先看看這一個(gè)例子:
# 例0 def foo(): exec('y = 1 + 1') z = locals()['y'] print(z) foo() # 輸出:2
exec() 函數(shù)的代碼塊中定義了變量 y,這個(gè)值可以被隨后的 locals() 取到,在賦值后也打印了出來。然而,在這個(gè)例子的基礎(chǔ)上,只需做出小小的改變,結(jié)果就可能大不相同了。
# 例1 def foo(): exec('y = 1 + 1') y = locals()['y'] print(y) foo() # 報(bào)錯(cuò):KeyError: 'y'
把前例的 z 改為 y ,就報(bào)錯(cuò)了。其中,KeyError 指的是在字典中不存在對應(yīng)的 key 。為什么會這樣呢,新賦值的變量是 y 或者 z,為什么對結(jié)果有這么不同的影響?
試試把 exec 去掉,不報(bào)錯(cuò)!
# 例2 def foo(): y = 1 + 1 y = locals()['y'] print(y) foo() # 2
問題:直接對 y 賦值,跟動態(tài)地在 exec() 中賦值,會對 locals() 取值產(chǎn)生怎樣的影響?
再試試對例 1 的 locals() 先賦值,還是報(bào)錯(cuò):
# 例3 def foo(): exec('y = 1 + 1') boc = locals() y = boc['y'] print(y) foo() # KeyError: 'y'
先做一次賦值,難道沒有用么?也不是,如果把賦值的順序調(diào)前,就不報(bào)錯(cuò)了:
# 例4 def foo(): boc = locals() exec('y = 1 + 1') y = boc['y'] print(y) foo() # 2
也就是說,locals() 的值并不是固定的,它的值與調(diào)用時(shí)的上下文相關(guān),調(diào)用 locals() 的時(shí)機(jī)至關(guān)重要。
然而,如果想要驗(yàn)證一下,在函數(shù)中增加一個(gè) locals() 的打印,這個(gè)動作卻會影響到最終的執(zhí)行結(jié)果。
# 例5 def foo(): boc = locals() exec('y = 1 + 1') print(locals()) y = boc['y'] print(y) foo() # {'boc': {...}} # KeyError: 'y'
這到底是怎么回事呢?
以上例子在細(xì)微之處有較大的不同,主要由于以下知識點(diǎn)的影響:
1、變量的聲明與賦值
2、locals() 取值與修改的邏輯
3、locals() 字典與局部命名空間的關(guān)系
4、函數(shù)的編譯,抽象語法樹的解析
注意:exec() 函數(shù)有兩個(gè)缺省的參數(shù) globals() 與 locals() (與內(nèi)置函數(shù)同名),起的是限定字符串參數(shù)中變量的作用,若添加出來,只會增加以上例子的復(fù)雜度,因此,我們都做缺省處理,這里討論的是 exec() 只有一個(gè)參數(shù)的情況。
在某些編程語言中,變量的聲明與賦值是可以分開的,例如在聲明時(shí)寫 int a ,需要賦值時(shí),再寫 a = 1 ,當(dāng)然也可不拆分,則是 int a = 1 。
對應(yīng)到 Python 中,情況就不同了,這兩個(gè)動作在書寫時(shí)是合二為一的。首先它不用指定變量的類型,任何時(shí)候都不需要(也不能)在變量前加類型(如 int),其次,聲明與賦值過程無法拆分書寫,即只能寫成 a = 1 這樣??雌饋硭渌Z言的賦值寫法一樣,但實(shí)際上,它的效果是 int a = 1 。
這雖然是一種便利,但也隱藏了一個(gè)不易察覺的陷阱(劃重點(diǎn)):當(dāng)看到 a = 1 時(shí),你無法確定 a 是初次聲明的,還是已被聲明過的。
關(guān)于 locals() 的創(chuàng)建過程,在《Python 動態(tài)賦值的陷阱》文中有所分析,locals() 字典是局部命名空間的代理,它會采集局部作用域的變量,代碼運(yùn)行期若動態(tài)修改局部變量,只會影響該字典,并不會影響真正的局部作用域的變量。因此,當(dāng)再次調(diào)用 locals() 時(shí),由于重新采集,則動態(tài)修改的內(nèi)容會被丟棄。
運(yùn)行期的局部命名空間不可改變,這意味著 exec() 函數(shù)中的變量賦值不會對它產(chǎn)生影響,但 locals() 字典是可變的,會受到 exec() 函數(shù)的影響。
關(guān)于函數(shù)的編譯,我在《Python與家國天下》中寫到了對抽象語法樹的分析,Python 在編譯時(shí)就確定了局部作用域內(nèi)合法的變量名,在運(yùn)行時(shí)再與內(nèi)容綁定。作用域內(nèi)變量的解析跟它的執(zhí)行順序無關(guān),更與是否會被執(zhí)行無關(guān)。
以上內(nèi)容是前提,友情提示,如你有理解模糊之處,請先閱讀對應(yīng)的文章。接下來則是基于這些內(nèi)容而作的分析。
我不敢保證每個(gè)細(xì)節(jié)都準(zhǔn)確無誤,但這個(gè)分析力求達(dá)到深入淺出、面面俱到、邏輯自恰,而且順便幽默有趣……
例 0 中,局部作用域內(nèi)雖然沒有 ‘y’,但 exec() 函數(shù)動態(tài)創(chuàng)建了它,因此動態(tài)地寫入了 locals() 字典中,所以能查找到而不報(bào)錯(cuò)。
例 1 中,exec() 不影響局部作用域,即此時(shí) y 未在局部作用域內(nèi)做過聲明與賦值,接下來的一句才是第一次在局部作用域中對 y 作聲明與賦值 !
y = locals()['y'] ,等號左側(cè)在做聲明,只要等號右側(cè)的結(jié)果成立,整個(gè)聲明與賦值的過程就成立。右側(cè)需在 locals() 字典中查找 y 對應(yīng)的值。
在創(chuàng)建 locals() 字典時(shí),由于局部作用域內(nèi)有變量 y 的聲明,因此我們首先在其中采集到了 y,而不必在 exec() 函數(shù)的動態(tài)結(jié)果中查找。這就有了字典的一個(gè) key,接著要匹配這個(gè) key 對應(yīng)的值,也即 y 所綁定的值。
但是,剛才說了這是 y 的第一次賦值,并未完成呢,因此 y 并無有效的綁定值。
矛盾出現(xiàn)了,這里有點(diǎn)繞,我們理一下:左側(cè)的 y 等著完成賦值,因此需要右側(cè)的執(zhí)行結(jié)果;而右側(cè)的字典需要使用到 y 的值,因此就依賴著左側(cè)的 y 完成賦值。兩邊的操作都未完成,但雙方都需要依賴對方先完成,這是個(gè)無法破解的死局。
可以說,y 的值是一團(tuán)混沌,它必然等于 “l(fā)ocals()['y']” ,然而只有解開這團(tuán)代碼才能確切得到結(jié)果——只有打開籠子才知道結(jié)果,你是否想到了薛定諤的那只貓呢?
locals() 字典雖然拿到了 y 的名,卻拿不到它的實(shí),空歡喜一場,所以報(bào) KeyError。
例 3 同理,未完成賦值就使用,所以報(bào)錯(cuò)。
例 2 中,y 在二次賦值的過程時(shí),局部命名空間中已經(jīng)存在著有效的 y 等于 2,因此 locals() 查找到它而用于賦值,所以不報(bào)錯(cuò)。
至于例 4,它跟例 3 只差了一個(gè)執(zhí)行順序,為什么不會報(bào)錯(cuò)呢?還有更奇怪的,在例 4 上再加一個(gè)打?。ɡ?),理應(yīng)不會影響結(jié)果,可事實(shí)卻是又報(bào)錯(cuò)了,為什么?
例 4 中,boc = locals() 這句同樣存在循環(huán)引用的問題,因此執(zhí)行后的字典中沒有 y,接著 exec() 這句動態(tài)地修改了 locals(),執(zhí)行后 boc 的結(jié)果是 {'y' : 2},因此再下一句的 boc['y'] 能查找到結(jié)果,而不報(bào)錯(cuò)。
例 4 與例 3 的 ”y = boc['y']“ ,雖然都是第一次在局部作用域中聲明與賦值 y,但例 4 的 boc 已被 exec() 修改過,因此它能取到實(shí)實(shí)在在的值,就不再有循環(huán)引用的問題了。
接著看例 5,第一個(gè) locals() 還是存在循環(huán)引用現(xiàn)象,接著 exec() 往字典中寫入變量 y,但是,第二個(gè) locals() 又觸發(fā)了新的創(chuàng)建字典過程,會把 exec() 的執(zhí)行結(jié)果覆蓋,因此進(jìn)入第二輪循環(huán)引用,導(dǎo)致報(bào)錯(cuò)。
例 5 與例 4 的不同在于,它是根據(jù)局部作用域重新生成的字典,其效果等同于例 3。
另外,請?zhí)貏e注意打印的結(jié)果:{'boc': {…}} 。
這個(gè)結(jié)果說明,第二個(gè) locals() 是一個(gè)字典,而且它只有唯一的 key 是 ’boc‘,而 ’boc‘ 映射的是第一個(gè) locals() 字典,也即是 {…} 。這個(gè)寫法表示它內(nèi)部出現(xiàn)了循環(huán)引用,直觀地證實(shí)了前面的所有分析。
字典內(nèi)部出現(xiàn)循環(huán)引用 ,這個(gè)現(xiàn)象極其罕見!前面雖然做了分析,但看到這里的時(shí)候,不知道你是否覺得不可思議?
之所以第一次的循環(huán)引用能被記錄下來,原因在于我們沒有試圖去取出 ’y‘ 的值,而第二個(gè)循環(huán)引用則由于取值報(bào)錯(cuò)而無法記錄下來。
這個(gè)例子告訴大家:薛定諤的貓混入了 Python 的字典中,而且答案是,打開籠子,這只貓就會死亡。
字典的循環(huán)引用現(xiàn)象在幾個(gè)例子中扮演了極其重要的角色,但是往往被人忽視。之所以難以被人覺察,原因還是前面劃重點(diǎn)的內(nèi)容:當(dāng)看到 a = 1 時(shí),你無法確定 a 是初次聲明的,還是已被聲明過的。
在《Python與家國天下》文中,貓哥分析了兩類經(jīng)典的報(bào)錯(cuò):name 'x' is not defined、local variable 'x' referenced before assignment。它們通常也是由于聲明與賦值不分,而導(dǎo)致的失察。
本文中的 KeyError 實(shí)際上就是“l(fā)ocal variable 'y' referenced before assignment”,y已defined 而未 assigned,導(dǎo)致 reference 時(shí)報(bào)錯(cuò)。
已賦值還是未賦值,這是個(gè)問題。也是一只貓。
最后,盡管這只貓?jiān)诎抵袚v了大亂,我們還是要感謝它:感謝它串聯(lián)了其它知識被我們“一鍋端”,感謝它為這篇抽象燒腦的文章?lián)铣隽藥追只顫娚鷦拥娜の丁ㄒ约埃兄x它帶來的標(biāo)題靈感,不知道有多少人是沖著標(biāo)題而閱讀的?)
數(shù)據(jù)分析咨詢請掃描二維碼
若不方便掃碼,搜微信號:CDAshujufenxi
LSTM 模型輸入長度選擇技巧:提升序列建模效能的關(guān)鍵? 在循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)家族中,長短期記憶網(wǎng)絡(luò)(LSTM)憑借其解決長序列 ...
2025-07-11CDA 數(shù)據(jù)分析師報(bào)考條件詳解與準(zhǔn)備指南? ? 在數(shù)據(jù)驅(qū)動決策的時(shí)代浪潮下,CDA 數(shù)據(jù)分析師認(rèn)證愈發(fā)受到矚目,成為眾多有志投身數(shù) ...
2025-07-11數(shù)據(jù)透視表中兩列相乘合計(jì)的實(shí)用指南? 在數(shù)據(jù)分析的日常工作中,數(shù)據(jù)透視表憑借其強(qiáng)大的數(shù)據(jù)匯總和分析功能,成為了 Excel 用戶 ...
2025-07-11尊敬的考生: 您好! 我們誠摯通知您,CDA Level I和 Level II考試大綱將于 2025年7月25日 實(shí)施重大更新。 此次更新旨在確保認(rèn) ...
2025-07-10BI 大數(shù)據(jù)分析師:連接數(shù)據(jù)與業(yè)務(wù)的價(jià)值轉(zhuǎn)化者? ? 在大數(shù)據(jù)與商業(yè)智能(Business Intelligence,簡稱 BI)深度融合的時(shí)代,BI ...
2025-07-10SQL 在預(yù)測分析中的應(yīng)用:從數(shù)據(jù)查詢到趨勢預(yù)判? ? 在數(shù)據(jù)驅(qū)動決策的時(shí)代,預(yù)測分析作為挖掘數(shù)據(jù)潛在價(jià)值的核心手段,正被廣泛 ...
2025-07-10數(shù)據(jù)查詢結(jié)束后:分析師的收尾工作與價(jià)值深化? ? 在數(shù)據(jù)分析的全流程中,“query end”(查詢結(jié)束)并非工作的終點(diǎn),而是將數(shù) ...
2025-07-10CDA 數(shù)據(jù)分析師考試:從報(bào)考到取證的全攻略? 在數(shù)字經(jīng)濟(jì)蓬勃發(fā)展的今天,數(shù)據(jù)分析師已成為各行業(yè)爭搶的核心人才,而 CDA(Certi ...
2025-07-09【CDA干貨】單樣本趨勢性檢驗(yàn):捕捉數(shù)據(jù)背后的時(shí)間軌跡? 在數(shù)據(jù)分析的版圖中,單樣本趨勢性檢驗(yàn)如同一位耐心的偵探,專注于從單 ...
2025-07-09year_month數(shù)據(jù)類型:時(shí)間維度的精準(zhǔn)切片? ? 在數(shù)據(jù)的世界里,時(shí)間是最不可或缺的維度之一,而year_month數(shù)據(jù)類型就像一把精準(zhǔn) ...
2025-07-09CDA 備考干貨:Python 在數(shù)據(jù)分析中的核心應(yīng)用與實(shí)戰(zhàn)技巧? ? 在 CDA 數(shù)據(jù)分析師認(rèn)證考試中,Python 作為數(shù)據(jù)處理與分析的核心 ...
2025-07-08SPSS 中的 Mann-Kendall 檢驗(yàn):數(shù)據(jù)趨勢與突變分析的有力工具? ? ? 在數(shù)據(jù)分析的廣袤領(lǐng)域中,準(zhǔn)確捕捉數(shù)據(jù)的趨勢變化以及識別 ...
2025-07-08備戰(zhàn) CDA 數(shù)據(jù)分析師考試:需要多久?如何規(guī)劃? CDA(Certified Data Analyst)數(shù)據(jù)分析師認(rèn)證作為國內(nèi)權(quán)威的數(shù)據(jù)分析能力認(rèn)證 ...
2025-07-08LSTM 輸出不確定的成因、影響與應(yīng)對策略? 長短期記憶網(wǎng)絡(luò)(LSTM)作為循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)的一種變體,憑借獨(dú)特的門控機(jī)制,在 ...
2025-07-07統(tǒng)計(jì)學(xué)方法在市場調(diào)研數(shù)據(jù)中的深度應(yīng)用? 市場調(diào)研是企業(yè)洞察市場動態(tài)、了解消費(fèi)者需求的重要途徑,而統(tǒng)計(jì)學(xué)方法則是市場調(diào)研數(shù) ...
2025-07-07CDA數(shù)據(jù)分析師證書考試全攻略? 在數(shù)字化浪潮席卷全球的當(dāng)下,數(shù)據(jù)已成為企業(yè)決策、行業(yè)發(fā)展的核心驅(qū)動力,數(shù)據(jù)分析師也因此成為 ...
2025-07-07剖析 CDA 數(shù)據(jù)分析師考試題型:解鎖高效備考與答題策略? CDA(Certified Data Analyst)數(shù)據(jù)分析師考試作為衡量數(shù)據(jù)專業(yè)能力的 ...
2025-07-04SQL Server 字符串截取轉(zhuǎn)日期:解鎖數(shù)據(jù)處理的關(guān)鍵技能? 在數(shù)據(jù)處理與分析工作中,數(shù)據(jù)格式的規(guī)范性是保證后續(xù)分析準(zhǔn)確性的基礎(chǔ) ...
2025-07-04CDA 數(shù)據(jù)分析師視角:從數(shù)據(jù)迷霧中探尋商業(yè)真相? 在數(shù)字化浪潮席卷全球的今天,數(shù)據(jù)已成為企業(yè)決策的核心驅(qū)動力,CDA(Certifie ...
2025-07-04CDA 數(shù)據(jù)分析師:開啟數(shù)據(jù)職業(yè)發(fā)展新征程? ? 在數(shù)據(jù)成為核心生產(chǎn)要素的今天,數(shù)據(jù)分析師的職業(yè)價(jià)值愈發(fā)凸顯。CDA(Certified D ...
2025-07-03