函數(shù)內(nèi)全局變量引用的理解
自學(xué)中發(fā)現(xiàn)的理解問題
// 請把代碼文本粘貼到下方(請勿用圖片代替代碼)
c = 1
def test():
c= c+1
print(c)
test()
錯誤:輸出c未被初始化
c = 1
def test():
a= c+1
print(a)
test()
輸出:2
python賦值,先進行=右邊的計算,進行了c的引用,再進行了c的賦值,就報錯了,變量必須先賦值在引用。
和全局掛鉤理解是不是:
一般情況下:函數(shù)內(nèi)部可以調(diào)用全局的引用,但不能對其賦值, 除非global,nonlocal或者參數(shù)傳入
理解的都沒問題。但我想聊聊出現(xiàn)這種情況的原因。在講原因之前,需要先知道python中變量的搜索順序,這個順序是 LGB
(不考慮閉包情況)即local本地,global全局,builtin內(nèi)建。比如:
a = 1
def test():
a = 3
print(a)
test()
函數(shù)內(nèi)聲明了局部變量 a
,在打印中使用,在本地環(huán)境中命中,因此使用的是 3。
也許你會問這個知識點我早就知道了,這和本問題有什么關(guān)系呢?
有的,難道你不覺得奇怪嗎?報錯是變量未初始化,而不是變量未定義。
題目中函數(shù)內(nèi) c= c+1
就已經(jīng)表明了聲明的變量 c 是屬于局部變量的。 按理說,先執(zhí)行賦值語句右側(cè),而此時 c 并沒有聲明,應(yīng)該在全局環(huán)境命中才對啊。所以想象中的結(jié)果應(yīng)該是局部變量 c = 2
而全局變量的 c 保持原值。
但是,這只都是想當(dāng)然。講了這么多其實我是想引出,python雖然是動態(tài)語句,但它還是會對代碼做掃描工作的,會有收集有用的靜態(tài)信息。函數(shù)的應(yīng)該信息會放在 code
對象中,里面的信息就包含了局部變量名稱的集合,可以通過 co_varnames
得到,如下:
c = 1
def test():
c= c+1
a = 3 # 另一個局部變量
print(c)
print(test.__code__.co_varnames) # ('c', 'a')
因此,函數(shù)test在執(zhí)行前,變量 c 就已經(jīng)被聲明在局部變量環(huán)境中了,而不是我們自認為的當(dāng)賦值語句運行后才會在局部變量里。于是,這就導(dǎo)致了報錯信息是變量未初始化而不是變量未定義。
====== 分割線 =========
題主評論要求:
關(guān)于變量的初始化,定義,創(chuàng)建這三者關(guān)系,能幫忙疏導(dǎo)一下理解嗎?
這三個意思基本差不多,沒必要分得太開。把變量環(huán)境理解成一個字典 name_env = dict()
其實就很好理解了(事實上python底層也確實是這樣處理的)。對于在這個環(huán)境內(nèi)要創(chuàng)建一個名為 a
的變量,就可以是 name_env['a'] = value
的形式了。
這個過程也就是賦值語句形如 a = value
時會調(diào)用賦值的指令 STORE_NAME
。我們看一下這個賦值過程你就理解了。
這部分代碼在 ceval.c
中,詳見 ceval.c
TARGET(STORE_NAME) {
PyObject *name = GETITEM(names, oparg);
PyObject *v = POP();
PyObject *ns = f->f_locals;
int err;
if (ns == NULL) {
PyErr_Format(PyExc_SystemError,
"no locals found when storing %R", name);
Py_DECREF(v);
goto error;
}
if (PyDict_CheckExact(ns))
err = PyDict_SetItem(ns, name, v);
else
err = PyObject_SetItem(ns, name, v);
Py_DECREF(v);
if (err != 0)
goto error;
DISPATCH();
}
代碼不多,可以逐個分析下,第一行獲得的 name
就是賦值語句 a = value
的 a,a以python類型 str
形式存在。第二行 v
從棧中獲取,也就是 value
的值。第三行 ns
是從幀對象中獲得局部變量環(huán)境(大多數(shù)情況下是個字典類型,如果幀環(huán)境不在函數(shù)或類中,取得的是全局變量環(huán)境)。PyDict_SetItem(ns, name, v);
和 PyObject_SetItem(ns, name, v);
就可以理解為 ns[name] = v
和 setattr(ns, name, v)
了,創(chuàng)建過程就是這樣了。
變量究竟是創(chuàng)建還是初始化還是覆蓋已有的變量值,其實底層并不關(guān)心。
回到本地中,本地變量的符號表會保存在靜態(tài)信息里面,我猜測搜索變量時有優(yōu)先去靜態(tài)信息中得到信息,來更快的知道變量應(yīng)該是在局部還是全局中查找吧。
北大青鳥APTECH成立于1999年。依托北京大學(xué)優(yōu)質(zhì)雄厚的教育資源和背景,秉承“教育改變生活”的發(fā)展理念,致力于培養(yǎng)中國IT技能型緊缺人才,是大數(shù)據(jù)專業(yè)的國家
北大青鳥中博軟件學(xué)院創(chuàng)立于2003年,作為華東區(qū)著名互聯(lián)網(wǎng)學(xué)院和江蘇省首批服務(wù)外包人才培訓(xùn)基地,中博成功培育了近30000名軟件工程師走向高薪崗位,合作企業(yè)超4
中公教育集團創(chuàng)建于1999年,經(jīng)過二十年潛心發(fā)展,已由一家北大畢業(yè)生自主創(chuàng)業(yè)的信息技術(shù)與教育服務(wù)機構(gòu),發(fā)展為教育服務(wù)業(yè)的綜合性企業(yè)集團,成為集合面授教學(xué)培訓(xùn)、網(wǎng)
達內(nèi)教育集團成立于2002年,是一家由留學(xué)海歸創(chuàng)辦的高端職業(yè)教育培訓(xùn)機構(gòu),是中國一站式人才培養(yǎng)平臺、一站式人才輸送平臺。2014年4月3日在美國成功上市,融資1
曾工作于聯(lián)想擔(dān)任系統(tǒng)開發(fā)工程師,曾在博彥科技股份有限公司擔(dān)任項目經(jīng)理從事移動互聯(lián)網(wǎng)管理及研發(fā)工作,曾創(chuàng)辦藍懿科技有限責(zé)任公司從事總經(jīng)理職務(wù)負責(zé)iOS教學(xué)及管理工作。
浪潮集團項目經(jīng)理。精通Java與.NET 技術(shù), 熟練的跨平臺面向?qū)ο箝_發(fā)經(jīng)驗,技術(shù)功底深厚。 授課風(fēng)格 授課風(fēng)格清新自然、條理清晰、主次分明、重點難點突出、引人入勝。
精通HTML5和CSS3;Javascript及主流js庫,具有快速界面開發(fā)的能力,對瀏覽器兼容性、前端性能優(yōu)化等有深入理解。精通網(wǎng)頁制作和網(wǎng)頁游戲開發(fā)。
具有10 年的Java 企業(yè)應(yīng)用開發(fā)經(jīng)驗。曾經(jīng)歷任德國Software AG 技術(shù)顧問,美國Dachieve 系統(tǒng)架構(gòu)師,美國AngelEngineers Inc. 系統(tǒng)架構(gòu)師。