約束 名字空間 作用域 之間的那些事 不管在什么編程語言, 都有作用域這個(gè)概念.作用域控制在它范圍內(nèi)代碼的生存周期, 包括名字和實(shí)體的綁定. 名字和實(shí)體的綁定, 我們可以理解成賦值. num = int_obj, 當(dāng)我們執(zhí)行這句代碼時(shí), 實(shí)際上我們已經(jīng)得到一個(gè)(‘num’, int_obj)的關(guān)聯(lián)關(guān)系, 我們也能將稱之為約束, 這個(gè)約束也將存在名字空間(name space)里面, 名字空間也將是LEGB查找的依據(jù). 而每個(gè)名字空間, 也將對應(yīng)一個(gè)作用域, 作用域是代碼正文中的一段代碼區(qū)域, 作用域的有效范圍更多是這段代碼區(qū)域去衡量,一個(gè)作用域可以有多個(gè)名字空間, 一個(gè)名字空間也能有多個(gè)約束(多個(gè)賦值語句) 可以通過sys._getframe().f_code.co_name 查看代碼所處的作用域, 先來看下sys._getframe是什么鬼吧?
從函數(shù)的定義可以看到, sys._getframe將返回一個(gè)frameobject對象, 那其實(shí)frameobject是什么對象? 為什么它能決定作用域? frameobjec實(shí)際上就是python虛擬機(jī)上所維護(hù)的每個(gè)棧幀, 這和我們常規(guī)理解的棧幀多點(diǎn)差別, 因?yàn)閜ython在原有棧幀的基礎(chǔ)上, 在封裝一層形成自己的棧幀. 雖然是有些不同, 但是我們還是能近似看成常規(guī)理解的棧幀, 包括入棧,出棧 局部變量等等 那么frameobejct里面究竟有什么?
我們現(xiàn)在已經(jīng)知道frameobject的來歷呢, 那么再回顧上面提到的: sys._getframe().f_code.co_name 毫無疑問, 我們還是得看下codeobject是什么東西, 才能知道name的意思: 同樣也是print help大法
雖然 sys._getframe().f_code.co_name 頂多也只能說明, 這段代碼是在哪個(gè)code block里面, 并沒有直接證明就是作用域, 但是從上面也已經(jīng)談到, 作用域是從代碼正文的代碼片段的決定, So, 也能近似看成算是作用域的名字了~ 作用域話題似乎聊得有點(diǎn)深入了, 讓我們暫告一段落, 繼續(xù)講講 約束 和 作用域的關(guān)系吧 每個(gè)約束一旦創(chuàng)建, 將會持續(xù)的影響后面代碼的執(zhí)行, 但是約束也只能在名字空間內(nèi)生效, 也就是說,一旦出了名字空間/作用域. 約束也將失效
在上面例子可以看到, 變量a在模塊層和函數(shù)f層都有賦值, 在執(zhí)行函數(shù)f時(shí),輸出6, 但是在下面卻輸出了3, 也就是因?yàn)楹瘮?shù)f 中的 a=3 約束只有在函數(shù)f的作用域中生效,函數(shù)結(jié)束,a的值, 應(yīng)該是最開始的a=3來控制, 我們現(xiàn)在應(yīng)該隱約有種感覺, 為什么賦值語句會被稱為約束? 我們完全可以理解成, 一個(gè)變量名, 可能有多次改變其綁定的實(shí)體對象的機(jī)會, 但是最終顯示是哪個(gè)實(shí)體, 完全就是從作用域->名字空間->約束 來決定
LEGB 從上面我們已經(jīng)清楚 約束,名字空間, 作用域之間微妙的關(guān)系, 那么我們接下來就應(yīng)該探討下變量查找的方式了. LEGB 分別是:
而查找的優(yōu)先順序從左到右以此是: L -> E -> G -> B 從上面我們已經(jīng)知道, 約束, 是受作用域和名字空間的影響, 所以查找肯定也是只能在名字空間去進(jìn)行 來些簡單代碼吧:
這段相信大家都知道為什么能夠輸出3, 當(dāng)在函數(shù)內(nèi)部的名字空間找不到關(guān)于變量a的約束時(shí), 將會去全局變量的名字空間查到, OK, 已經(jīng)找到了 (a,3)的約束, 返回 3., test()也是同理 同樣的, 在函數(shù)內(nèi)部和模塊內(nèi)部都不能找到open的約束, 那么只能去Bulitin(內(nèi)置名字空間)去查找了, 找到了open了, 并且還是個(gè)函數(shù), 所以返回 簡單的演示完, 來些神奇的代碼:
有沒有覺得很奇怪, a=4是在函數(shù)f里面定義的, 但是返回v的時(shí)候, 函數(shù)已經(jīng)退出,理應(yīng)釋放了, 為什么test()還能輸出4呢? 其實(shí)原因很簡單, 首先這個(gè)已經(jīng)是閉包函數(shù)了, 同樣的還是遵循LEGB的原則, 函數(shù)v已經(jīng)能夠在外層嵌套作用域找到a的定義, 又因?yàn)殚]包函數(shù)有個(gè)特點(diǎn), 在構(gòu)建的時(shí)候, 能夠?qū)⑿枰募s束也一并綁定到自身里頭, 所以即使函數(shù)f退出了, 變量a釋放了, 但是不要緊, 函數(shù)v已經(jīng)綁定好了相應(yīng)的約束了, 自然而然也就能輸出4,。 |
|