在程序中定義一個(gè)變量時(shí)变泄,這個(gè)變量是有作用范圍的令哟,變量的作用范圍被稱為它的作用域。
根據(jù)定義變量的位置妨蛹,變量分為兩種:
局部變量:在函數(shù)中定義的變量屏富,包括參數(shù),都被稱為局部變量蛙卤。
全局變量:在函數(shù)外面狠半、全局范圍內(nèi)定義的變量,被稱為全局變量颤难。
每個(gè)函數(shù)在執(zhí)行時(shí)神年,系統(tǒng)都會(huì)為該函數(shù)分配一塊“臨時(shí)內(nèi)存空間”,所有的局部變量都被保存在這塊臨時(shí)內(nèi)存空間內(nèi)行嗤。當(dāng)函數(shù)執(zhí)行完成后已日,這塊內(nèi)存空間就被釋放了,這些局部變量也就失效了栅屏,因此離開函數(shù)之后就不能再訪問局部變量了飘千。
全局變量意味著它們可以在所有函數(shù)內(nèi)被訪問堂鲜。
不管是在函數(shù)的局部范圍內(nèi)還是在全局范圍內(nèi),都可能存在多個(gè)變量护奈,每個(gè)變量“持有”該變量的值缔莲。從這個(gè)角度來看,不管是局部范圍還是全局范圍霉旗,這些變量和它們的值就像一個(gè)“看不見”的字典酌予,其中變量名就是字典的 key,變量值就是字典的 value奖慌。
實(shí)際上抛虫,Python 提供了如下三個(gè)工具函數(shù)來獲取指定范圍內(nèi)的“變量字典”:
globals():該函數(shù)返回全局范圍內(nèi)所有變量組成的“變量字典”。
locals():該函數(shù)返回當(dāng)前局部范圍內(nèi)所有變量組成的“變量字典”简僧。
vars(object):獲取在指定對(duì)象范圍內(nèi)所有變量組成的“變量字典”建椰。如果不傳入object 參數(shù),vars() 和 locals() 的作用完全相同岛马。
globals() 和 locals() 看似完全不同棉姐,但它們實(shí)際上也是有聯(lián)系的,關(guān)于這兩個(gè)函數(shù)的區(qū)別和聯(lián)系大致有以下兩點(diǎn):
locals() 總是獲取當(dāng)前局部范圍內(nèi)所有變量組成的“變量字典”啦逆,因此伞矩,如果在全局范圍內(nèi)(在函數(shù)之外)調(diào)用 locals() 函數(shù),同樣會(huì)獲取全局范圍內(nèi)所有變量組成的“變量字典”夏志;而 globals() 無論在哪里執(zhí)行乃坤,總是獲取全局范圍內(nèi)所有變量組成的“變量字典”。
一般來說沟蔑,使用 locals() 和 globals() 獲取的“變量字典”只應(yīng)該被訪問湿诊,不應(yīng)該被修改。但實(shí)際上瘦材,不管是使用 globals() 還是使用 locals() 獲取的全局范圍內(nèi)的“變量字典”厅须,都可以被修改,而這種修改會(huì)真正改變?nèi)肿兞勘旧恚旱ㄟ^ locals() 獲取的局部范圍內(nèi)的“變量字典”食棕,即使對(duì)它修改也不會(huì)影響局部變量朗和。
下面程序示范了如何使用 locals()、globals() 函數(shù)訪問局部范圍和全局范圍內(nèi)的“變量字典”:
def?test?():
age?=?20
#?直接訪問age局部變量
print(age)?#?輸出20
#?訪問函數(shù)局部范圍的“變量數(shù)組”
print(locals())?#?{'age':?20}
#?通過函數(shù)局部范圍的“變量數(shù)組”訪問age變量
print(locals()['age'])?#?20
#?通過locals函數(shù)局部范圍的“變量數(shù)組”改變age變量的值
locals()['age']?=?12
#?再次訪問age變量的值
print('xxx',?age)?#?依然輸出20
#?通過globals函數(shù)修改x全局變量
globals()['x']?=?19
x?=?5
y?=?20
print(globals())?#?{...,?'x':?5,?'y':?20}
#?在全局訪問內(nèi)使用locals函數(shù)簿晓,訪問的是全局變量的“變量數(shù)組”
print(locals())?#?{...,?'x':?5,?'y':?20}
#?直接訪問x全局變量
print(x)?#?5
#?通過全局變量的“變量數(shù)組”訪問x全局變量
print(globals()['x'])?#?5
#?通過全局變量的“變量數(shù)組”對(duì)x全局變量賦值
globals()['x']?=?39
print(x)?#?輸出39
#?在全局范圍內(nèi)使用locals函數(shù)對(duì)x全局變量賦值
locals()['x']?=?99
print(x)?#?輸出99
從上面程序可以清楚地看出眶拉,locals() 函數(shù)用于訪問特定范圍內(nèi)的所有變量組成的“變量字典”,而 globals() 函數(shù)則用于訪問全局范圍內(nèi)的全局變量組成的“變量字典”抢蚀。
全局變量默認(rèn)可以在所有函數(shù)內(nèi)被訪問镀层,但如果在函數(shù)中定義了與全局變量同名的變量,此時(shí)就會(huì)發(fā)生局部變量遮蔽(hide)全局變量的情形皿曲。例如如下程序:
name?=?'Charlie'
def?test?():
#?直接訪問name全局變量
print(name)?#?Charlie
test()
print(name)
上面程序中唱逢,第 4 行直接訪問 name 變量,這是允許的屋休,此時(shí)程序?qū)?huì)輸出 Charlie坞古。如果在此之后增加如下一行代碼:
name?=?'孫悟空'
再次運(yùn)行該程序,將會(huì)看到如下錯(cuò)誤:
UnboundLocalError?:?local?variable?‘name’?referenced?before?assignment
該錯(cuò)誤提示粗體字代碼所訪問的 name 變量還未定義劫樟。這是什么原因呢痪枫?這正是由于程序在 test() 函數(shù)中增加了“name='孫悟空'”一行代碼造成的。
Python 語法規(guī)定叠艳,在函數(shù)內(nèi)部對(duì)不存在的變量賦值時(shí)奶陈,默認(rèn)就是重新定義新的局部變量。因此這行代碼相當(dāng)于重新定義了 name 局部變量附较,這樣 name 全局變量就被遮蔽了吃粒,所以程序會(huì)報(bào)錯(cuò)。
為了避免這個(gè)問題拒课,可以通過以下兩種方式來修改上面程序:
訪問被遮蔽的全局變量徐勃。如果希望程序依然能訪問 name 全局變量,且在函數(shù)中可重新定義 name 局部變量早像,也就是在函數(shù)中可以訪問被遮蔽的全局變量僻肖,此時(shí)可通過 globals() 函數(shù)來實(shí)現(xiàn),將上面程序改為如下形式即可:
name?=?'Charlie'
def?test?():
#?直接訪問name全局變量
print(globals()['name'])??#?Charlie
name?=?'孫悟空'
test()
print(name)??#?Charlie
在函數(shù)中聲明全局變量卢鹦。為了避免在函數(shù)中對(duì)全局變量賦值(不是重新定義局部變量)臀脏,可使用 global 語句來聲明全局變量。因此冀自,可將程序改為如下形式:
name?=?'Charlie'
def?test?():
#?聲明name是全局變量谁榜,后面的賦值語句不會(huì)重新定義局部變量
global?name
#?直接訪問name全局變量
print(name)??#?Charlie
name?=?'孫悟空'
test()
print(name)??#?孫悟空
增加了“global name”聲明之后,程序會(huì)把 name 變量當(dāng)成全局變量凡纳,這意味著 test() 函數(shù)后面對(duì) name 賦值的語句只是對(duì)全局變量賦值窃植,而不是重新定義局部變量。