Python的命名空間通過一種字典的形式來體現, 而具體到函數也就是locals() 和 globals(), 分別對應著局部命名空間和全局命名空間. 于是, 我們也就能通過這些方法去實現我們"動態賦值"的需求.
例如:
deftest():globals()['a2']=4test()printa2#輸出4
很自然, 既然 globals能改變全局命名空間, 那理所當然locals應該也能修改局部命名空間.修改函數內的局部變量.
但事實真是如此嗎? 不是!
defaaaa():printlocals()foriin['a','b','c']:locals()[i]=1printlocals()printaaaaa()
輸出:
{}
{'i': 'c', 'a': 1, 'c': 1, 'b': 1}
Traceback (most recent call last):
File "5.py", line 17, in <module>
aaaa()
File "5.py", line 16, in aaaa
print a
NameError: global name 'a' is not defined
程序運行報錯了!
但是在第二次print locals()很清楚能夠看到, 局部空間是已經有那些變量了, 其中也有變量a并且值也為1, 但是為什么到了print a卻報出NameError異常?
再看一個例子:
defaaaa():printlocals()s='test'#加入顯示賦值sforiin['a','b','c']:locals()[i]=1printlocals()prints#打印局部變量sprintaaaaa()
輸出:
{}
{'i': 'c', 'a': 1, 's': 'test', 'b': 1, 'c': 1}
test
Traceback (most recent call last):
File "5.py", line 19, in <module>
aaaa()
File "5.py", line 18, in aaaa
print a
NameError: global name 'a' is not defined
上下兩段代碼, 區別就是, 下面的有顯示賦值的代碼, 雖然也是同樣觸發了NameError異常, 但是局部變量s的值被打印了出來.
這就讓我們覺得很納悶, 難道通過locals()改變局部變量, 和直接賦值有不同? 想解決這個問題, 只能去看程序運行的真相了, 又得上大殺器dis~
根源探討
直接對第二段代碼解析:
130LOAD_GLOBAL0(locals)3CALL_FUNCTION06PRINT_ITEM7PRINT_NEWLINE148LOAD_CONST1('test')11STORE_FAST0(s)1514SETUP_LOOP36(to53)17LOAD_CONST2('a')20LOAD_CONST3('b')23LOAD_CONST4('c')26BUILD_LIST329GET_ITER>>30FOR_ITER19(to52)33STORE_FAST1(i)1636LOAD_CONST5(1)39LOAD_GLOBAL0(locals)42CALL_FUNCTION045LOAD_FAST1(i)48STORE_SUBSCR49JUMP_ABSOLUTE30>>52POP_BLOCK17>>53LOAD_GLOBAL0(locals)56CALL_FUNCTION059PRINT_ITEM60PRINT_NEWLINE1861LOAD_FAST0(s)64PRINT_ITEM65PRINT_NEWLINE1966LOAD_GLOBAL1(a)69PRINT_ITEM70PRINT_NEWLINE71LOAD_CONST0(None)74RETURN_VALUENone
在上面的字節碼可以看到:
locals() 對應的字節碼是: LOAD_GLOBAL
s='test' 對應的字節碼是: LOAD_CONST 和 STORE_FAST
print s 對應的字節碼是: LOAD_FAST
print a 對應的字節碼是: LOAD_GLOBAL
從上面羅列出來的幾個關鍵語句的字節碼可以看出, 直接賦值/讀取 和 通過locals()賦值/讀取 本質是很大不同的. 那么觸發NameError異常, 是否證明通過 locals()[i] = 1 存儲的值, 和真正的局部命名空間 是不同的兩個位置?
想要回答這個問題, 我們得先確定一個東西, 就是真正的局部命名空間如何獲取? 其實這個問題, 在上面的字節碼上, 已經給出了標準答案了!
真正的局部命名空間, 其實是存在 STORE_FAST 這個對應的數據結構里面. 這個是什么鬼, 這個需要源碼來解答:
//ceval.c從上往下,依次是相應函數或者變量的定義//指令源碼TARGET(STORE_FAST){v=POP();SETLOCAL(oparg,v);FAST_DISPATCH();}--------------------//SETLOCAL宏定義#defineSETLOCAL(i,value)do{PyObject*tmp=GETLOCAL(i);\GETLOCAL(i)=value;\Py_XDECREF(tmp);}while(0)--------------------//GETLOCAL宏定義#defineGETLOCAL(i)(fastlocals[i])--------------------//fastlocals真面目PyObject*PyEval_EvalFrameEx(PyFrameObject*f,intthrowflag){//省略其他無關代碼fastlocals=f->f_localsplus;....}
看到這里, 應該就能明確了, 函數內部的局部命名空間, 實際是就是幀對象的f的成員f_localsplus, 這是一個數組, 了解函數創建的童鞋可能會比較清楚, 在CALL_FUNCTION時, 會對這個數組進行初始化, 將形參賦值什么都會按序塞進去, 在字節碼 18 61 LOAD_FAST 0 (s)中, 第四列的0, 就是將f_localsplus第 0 個成員取出來, 也就是值 "s".
所以STORE_FAST才是真正的將變量存入局部命名空間, 那locals()又是什么鬼? 為什么看起來就跟真的一樣?
這個就需要分析locals, 對于這個, 字節碼可能起不了作用, 直接去看內置函數如何定義的吧:
//bltinmodule.cstaticPyMethodDefbuiltin_methods[]={...//找到locals函數對應的內置函數是builtin_locals{"locals",(PyCFunction)builtin_locals,METH_NOARGS,locals_doc},...}-----------------------------//builtin_locals的定義staticPyObject*builtin_locals(PyObject*self){PyObject*d;d=PyEval_GetLocals();Py_XINCREF(d);returnd;}-----------------------------PyObject*PyEval_GetLocals(void){PyFrameObject*current_frame=PyEval_GetFrame();//獲取當前堆棧對象if(current_frame==NULL)returnNULL;PyFrame_FastToLocals(current_frame);//初始化和填充f_localsreturncurrent_frame->f_locals;}-----------------------------//初始化和填充f_locals的具體實現voidPyFrame_FastToLocals(PyFrameObject*f){/*Mergefastlocalsintof->f_locals*/PyObject*locals,*map;PyObject**fast;PyObject*error_type,*error_value,*error_traceback;PyCodeObject*co;Py_ssize_tj;intncells,nfreevars;if(f==NULL)return;locals=f->f_locals;//如果locals為空,就新建一個字典對象if(locals==NULL){locals=f->f_locals=PyDict_New();if(locals==NULL){PyErr_Clear();/*Can'treportit:-(*/return;}}co=f->f_code;map=co->co_varnames;if(!PyTuple_Check(map))return;PyErr_Fetch(&error_type,&error_value,&error_traceback);fast=f->f_localsplus;j=PyTuple_GET_SIZE(map);if(j>co->co_nlocals)j=co->co_nlocals;//將f_localsplus寫入localsif(co->co_nlocals)map_to_dict(map,j,locals,fast,0);ncells=PyTuple_GET_SIZE(co->co_cellvars);nfreevars=PyTuple_GET_SIZE(co->co_freevars);if(ncells||nfreevars){//將co_cellvars寫入localsmap_to_dict(co->co_cellvars,ncells,locals,fast+co->co_nlocals,1);if(co->co_flags&CO_OPTIMIZED){//將co_freevars寫入localsmap_to_dict(co->co_freevars,nfreevars,locals,fast+co->co_nlocals+ncells,1);}}PyErr_Restore(error_type,error_value,error_traceback);}
從上面PyFrame_FastToLocals已經看出來, locals() 實際上做了下面幾件事:
判斷幀對象 的 f_f->f_locals 是否為空, 若是, 則新建一個字典對象.
分別將 localsplus, co_cellvars 和 co_freevars 寫入 f_f->f_locals.
在這簡單介紹下上面幾個分別是什么鬼:
localsplus: 函數參數(位置參數+關鍵字參數), 顯示賦值的變量.
co_cellvars 和 co_freevars: 閉包函數會用到的局部變量.
以上就是Python中locals()的作用是什么,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注本站行業資訊頻道。
本文由 貴州做網站公司 整理發布,部分圖文來源于互聯網,如有侵權,請聯系我們刪除,謝謝!
c語言中正確的字符常量是用一對單引號將一個字符括起表示合法的字符常量。例如‘a’。數值包括整型、浮點型。整型可用十進制,八進制,十六進制。八進制前面要加0,后面...
2022年天津專場考試原定于3月19日舉行,受疫情影響確定延期,但目前延期后的考試時間推遲。 符合報名條件的考生,須在規定時間登錄招考資訊網(www.zha...
:喜歡聽,樂意看。指很受歡迎?!巴卣官Y料”喜聞樂見:[ xǐ wén lè jiàn ]詳細解釋1. 【解釋】:喜歡聽,樂意看。指很受歡迎。2. 【示例】:這是...
廣發證券開戶傭金一般為萬分之一2.5,起點5元,買賣雙向收取。營業部可根據投資者的資金量、交易量和委托方式綜合考慮設置,具體可咨詢開戶營業部。A股票的交易費用還包括印花稅和轉讓費:印花稅:交易金額的千分之一,只賣出收費,買入無印花稅;轉讓費:上海A股票雙向收取交易金額的十萬分之二。A股包含在傭金里面,不單獨收取。廣發證券開戶傭金套餐是什么?下面一起來看看其實,廣發證券開戶傭金套餐是傭金費率與服務內...
2022年全球十大商品貿易公司1、維多集團維多集團(Vitol Group)是全球更大的石油和天然氣實物貿易商,原油是其能源業務中最重要的部分。其他商品貿易包括:糖,金屬和谷物。維多集團每年運送超過3.5億噸原油,并將250艘超級油輪和其他船運往世界各地。 它平均每天處理超過700萬桶石油和產品-大約2、嘉能可國際嘉能可國際(Glencore ***ernational)是金屬,礦物和石油的更大貿...
“三去一降一補”指的是根據供給側結構性改革提出的去產能、去庫存、去杠桿、降成本、補短板。1、去產能:主要內容講的是鋼鐵、煤炭等行業,經審批該破產的就要破產關掉。那些失業了的職工,安排轉崗和再培訓再就業;2、去庫存:主要是指房地產開發中的空置樓。應出售空置建筑物并清除庫存。據說地方政府有去庫存任務指標;3、去杠桿:就是減少債務。政府、企業、個人都可以負債。當政府借錢從事某些項...