摘要:約束名字空間作用域之間的那些事不管在什么編程語言都有作用域這個(gè)概念作用域控制在它范圍內(nèi)代碼的生存周期包括名字和實(shí)體的綁定名字和實(shí)體的綁定我們可以理解成賦值當(dāng)我們執(zhí)行這句代碼時(shí)實(shí)際上我們已經(jīng)得到一個(gè)的關(guān)聯(lián)關(guān)系我們也能將稱之為約束這個(gè)約束也將存
約束 名字空間 作用域 之間的那些事
不管在什么編程語言, 都有作用域這個(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是什么鬼吧?
# sys module def _getframe(depth=None): # real signature unknown; restored from __doc__ """ _getframe([depth]) -> frameobject Return a frame object from the call stack. If optional integer depth is given, return the frame object that many calls below the top of the stack. If that is deeper than the call stack, ValueError is raised. The default for depth is zero, returning the frame at the top of the call stack. This function should be used for internal and specialized purposes only. """ pass
從函數(shù)的定義可以看到, sys._getframe將返回一個(gè)frameobject對象, 那其實(shí)frameobject是什么對象? 為什么它能決定作用域?
frameobjec實(shí)際上就是python虛擬機(jī)上所維護(hù)的每個(gè)棧幀, 這和我們常規(guī)理解的棧幀多點(diǎn)差別, 因?yàn)閜ython在原有棧幀的基礎(chǔ)上, 在封裝一層形成自己的棧幀. 雖然是有些不同, 但是我們還是能近似看成常規(guī)理解的棧幀, 包括入棧,出棧 局部變量等等
那么frameobejct里面究竟有什么?
# help(sys._getframe()) # Output: class frame(object) ..... # 省略 | Data descriptors defined here: | f_back # 上一個(gè)棧幀對象(誰調(diào)用自己) | f_builtins # 內(nèi)置名字空間 | f_locals # 全局名字空間 | f_globals # 全局名字空間 | f_code # 幀指向的 codeObject對象 ..... # 省略
我們現(xiàn)在已經(jīng)知道frameobject的來歷呢, 那么再回顧上面提到的: sys._getframe().f_code.co_name
毫無疑問, 我們還是得看下codeobject是什么東西, 才能知道name的意思:
同樣也是print help大法
# print help(sys._getframe().f_code) # Output: class code(object) ...... # 省略 | Data descriptors defined here: | | co_name # code block的名字, 通常是類名或者函數(shù)名 /* string (name, for reference) */ | | co_names # code block中所有的名字 /* list of strings (names used) */ | ...... # 省略
雖然 sys._getframe().f_code.co_name 頂多也只能說明, 這段代碼是在哪個(gè)code block里面, 并沒有直接證明就是作用域, 但是從上面也已經(jīng)談到, 作用域是從代碼正文的代碼片段的決定, So, 也能近似看成算是作用域的名字了~
作用域話題似乎聊得有點(diǎn)深入了, 讓我們暫告一段落, 繼續(xù)講講 約束 和 作用域的關(guān)系吧
每個(gè)約束一旦創(chuàng)建, 將會(huì)持續(xù)的影響后面代碼的執(zhí)行, 但是約束也只能在名字空間內(nèi)生效, 也就是說,一旦出了名字空間/作用域. 約束也將失效
a = 3 def f(): a = 6 print a # 輸出 6 f() print a # 輸出 3
在上面例子可以看到, 變量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)該隱約有種感覺, 為什么賦值語句會(huì)被稱為約束? 我們完全可以理解成, 一個(gè)變量名, 可能有多次改變其綁定的實(shí)體對象的機(jī)會(huì), 但是最終顯示是哪個(gè)實(shí)體, 完全就是從作用域->名字空間->約束 來決定
LEGB從上面我們已經(jīng)清楚 約束,名字空間, 作用域之間微妙的關(guān)系, 那么我們接下來就應(yīng)該探討下變量查找的方式了.
LEGB 分別是:
locals 是函數(shù)內(nèi)的名字空間,包括局部變量和形參
enclosing 外部嵌套函數(shù)的名字空間(閉包中常見)
globals 全局變量,函數(shù)定義所在模塊的名字空間
builtins 內(nèi)置模塊的名字空間
而查找的優(yōu)先順序從左到右以此是: L -> E -> G -> B
從上面我們已經(jīng)知道, 約束, 是受作用域和名字空間的影響, 所以查找肯定也是只能在名字空間去進(jìn)行
來些簡單代碼吧:
a = 3 def f(): print a # 輸出 3 print open # 輸出f() print "----------------------分割線----------------" a = 3 def f(): def v(): print a return v test = f() test() # 輸出 3
這段相信大家都知道為什么能夠輸出3, 當(dāng)在函數(shù)內(nèi)部的名字空間找不到關(guān)于變量a的約束時(shí), 將會(huì)去全局變量的名字空間查到, OK, 已經(jīng)找到了 (a,3)的約束, 返回 3., test()也是同理
同樣的, 在函數(shù)內(nèi)部和模塊內(nèi)部都不能找到open的約束, 那么只能去Bulitin(內(nèi)置名字空間)去查找了, 找到了open了, 并且還是個(gè)函數(shù), 所以返回
簡單的演示完, 來些神奇的代碼:
a = 3 def f(): a = 4 def v(): print a return v test = f() test() # 輸出 4 Why?
有沒有覺得很奇怪, 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
歡迎各位大神指點(diǎn)交流,轉(zhuǎn)載請注明: https://segmentfault.com/a/11...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/38573.html
摘要:真正管理這些名子的事物就是本文的主角命名空間。閉包命名空間閉包函數(shù)的名稱空間引入。函數(shù)調(diào)用時(shí)產(chǎn)生新的局部命名空間函數(shù)返回結(jié)果拋出異常時(shí)釋放命名空間,每一次遞歸都生成一個(gè)命名空間。標(biāo)識(shí)符產(chǎn)生地點(diǎn)決定標(biāo)識(shí)符所處的命名空間。 懶得掃全文的童鞋,可以直接跳到最后看總結(jié)。我們先從一個(gè)簡單的栗子說起: 栗子 a 文件中有變量 va 以及類 A,b 文件導(dǎo)入 a 中class A ,并打印出 A: ...
摘要:理解的名字空間的名字空間是一個(gè)非常核心的內(nèi)容。在中提供了一個(gè)關(guān)鍵字來修改外部嵌套函數(shù)的名字空間,但是要使用才有,我等使用的只能眼饞一下。 理解 Python 的 LEGB 名字空間 Python 的名字空間是 Python 一個(gè)非常核心的內(nèi)容。 其他語言中如 C 中,變量名是內(nèi)存地址的別名,而在 Python 中,名字是一個(gè)字符串對象,它與他指向的對象構(gòu)成一個(gè){name:obje...
摘要:當(dāng)程序引用某個(gè)變量的名字時(shí),就會(huì)從當(dāng)前名字空間開始搜索。對于可以看出已經(jīng)被導(dǎo)入到自己的名字空間了,而不是在里面。因此并沒有涉及到修改名字空間。按照原則,搜到有變量并且是個(gè)然后將其加入到自己的后面的就開始讀取的元素,并沒有影響的名字空間。 源自我的博客 前言 python里面最核心的內(nèi)容就是:名字空間(namespace) 例子引入 例1 #!/usr/bin/env python #...
摘要:例題核心編程第二版變量作用域和命名空間一節(jié)有以下一道題目請問輸出結(jié)果是什么要想解這道題,必須先了解中的一些概念的變量名解析機(jī)制有時(shí)稱為。 例題 《核心編程(第二版)》變量作用域和命名空間一節(jié)有以下一道題目 # coding=utf-8 #!/usr/bin/env python def proc1(): j,k = 3,4 print j == %d and k ==...
摘要:在函數(shù)中執(zhí)行賦值操作時(shí),會(huì)創(chuàng)建一個(gè)局部變量,如果想在函數(shù)中通過賦值改變一個(gè)全局變量,則需要用關(guān)鍵字申明,只要出現(xiàn)了操作符,則這個(gè)變量就是局部變量,除非顯示申明為。 python變量與變量作用域 c語言中,變量的定義會(huì)為變量分配一塊內(nèi)存,變量的內(nèi)存地址不會(huì)發(fā)生改變,當(dāng)變量的值發(fā)生改變時(shí),改變的是對應(yīng)內(nèi)存地址中的值。 python中,給變量賦值時(shí),變量保存的是一個(gè)對象的引用,如果想改變變...
閱讀 2277·2021-11-17 09:33
閱讀 2804·2021-11-12 10:36
閱讀 3434·2021-09-27 13:47
閱讀 922·2021-09-22 15:10
閱讀 3529·2021-09-09 11:51
閱讀 1441·2021-08-25 09:38
閱讀 2782·2019-08-30 15:55
閱讀 2640·2019-08-30 15:53