講這個(gè)知識(shí)點(diǎn)之前,先看一個(gè)例子: x=99 def func1(): global x x=88
def func2(): global x x=77 問題:大家覺得x最后的是值到底是多少:88還是77?先思考一下,最后我來揭曉謎底.... python的變量跟其他語言一樣,分為全局變量和局部變量,這個(gè)概念比較好理解,我們來看看python中是如何實(shí)現(xiàn)的: 1.全局變量與局部變量 x=100 def func(): print ('Inside func: x is {}'.format(x)) func() print 'x is still:{}'.format(x)
>> # 打印結(jié)果 Inside func: x is 100 x is still:100 這個(gè)比較好理解x是全局變量,作用域是整個(gè)文件,函數(shù)內(nèi)部是可以引用的.接著看下面一個(gè)例子: x=100 def func(): x=10 #多了這一行 print ('Changed local x to :{}'.format(x))
func() print 'x is still:{}'.format(x)
>>># 打印結(jié)果 Changed local x to :10 x is still:100 大家看在函數(shù)外部有一個(gè)變量x,在函數(shù)內(nèi)部也有一個(gè)變量x,這兩個(gè)變量雖然名字長的一樣,但是是完全不同的: 函數(shù)內(nèi)部的是本地變量,它的生命周期只在函數(shù)內(nèi)部,出了函數(shù)就結(jié)束了, 而x在函數(shù)外部模塊文件中聲明的(python一個(gè)文件也叫一個(gè)模塊),是全局變量,不會(huì)被函數(shù)里面的局部變量影響,所以最后print的x還是100, 有人要問了,有的時(shí)候我需要讓這個(gè)全局變量在函數(shù)里面處理,改變它的值,怎么辦?這個(gè)python早就考慮到了,往下看~~ 2.全局變量聲明: x=100 def func(): global x #注意加了一個(gè)global 關(guān)鍵字,表示x是全局作用域 print 'x is :{}'.format(x) x=10 print ('Changed local x to :{}'.format(x))
func() print 'Value of x:{}'.format(x)
>>> # 打印結(jié)果 x is :100 Changed local x to :10 Value of x:10 這個(gè)函數(shù)內(nèi)部多了一個(gè)global關(guān)鍵字,結(jié)果就差很多: 原因在于x被聲明為函數(shù)內(nèi)的全局變量,通過global這個(gè)語句是自己明確地映射到了模塊的作用域 函數(shù)內(nèi)對(duì)x重新賦值x=10,會(huì)改變函數(shù)外x的值,所以最后print x是10 全局變量簡單說就是這3點(diǎn): 全局變量是位于模塊文件內(nèi)部的頂層的變量名 全局變量如果是在函數(shù)內(nèi)被改變的話,一定要用global 全局變量名在函數(shù)內(nèi)部不經(jīng)過聲明也可以被引用 3.函數(shù)內(nèi)的變量解析原則 有的書上叫LEGB法則,其實(shí)講白了就是下面4個(gè)過程,當(dāng)在函數(shù)中使用沒有聲明過的變量時(shí),python的搜索順序是: 先是在函數(shù)內(nèi)部的本地作用域(L) 然后是在上一層的函數(shù)的本地作用域(E) 然后是全局作用域(G) 最后是內(nèi)置作用域(B) 簡單說就是從局部到中央,好比你找一個(gè)人,村里找不到找鄉(xiāng)->鄉(xiāng)里找不到找市里->市里找不到找到全國檔案局 我們來一一解釋一下這些原則 1).本地函數(shù) 在函數(shù)內(nèi)部(def或者lambda)通過任何方式賦值的,而且沒有在該函數(shù)內(nèi)聲明為全局變量的變量名 2).上層函數(shù)的本地作用域 python函數(shù)是支持嵌套,而且多層嵌套,當(dāng)你在最里層的函數(shù)找不到這個(gè)變量的時(shí)候,會(huì)往上一層的函數(shù)找,一層一層由內(nèi)往外找,舉個(gè)例子 def f1(): x=100 def f2(): print x f2()
print f1() >> 100 None #因?yàn)閒2()打印了之后沒有return,對(duì)沒有return的函數(shù)就默認(rèn)返回None 我來解釋一下: def定義了一個(gè)f1()函數(shù),里面又嵌套了一個(gè)f2()函數(shù),這個(gè)def生成了一個(gè)函數(shù)并將其賦值給變量名f2 f2是f1的本地作用域內(nèi)的一個(gè)本地變量,可以把f2看做一個(gè)臨時(shí)函數(shù),僅僅在f1內(nèi)部執(zhí)行的過程中存在. f2函數(shù)干了一件事打印x,當(dāng)在f2()內(nèi)部找不到的時(shí)候,就通過LEGB法則往上找,f1()里面找到了x. 3).全局(模塊) 在模塊文件的頂層賦值的變量名,或者在該文件中的def生成的名為全局變量的變量名(函數(shù)內(nèi)global聲明的變量) 4).內(nèi)置的作用域 這個(gè)很多初學(xué)者不明白,啥內(nèi)置,內(nèi)置了什么,其實(shí)很簡單,python在運(yùn)行之前會(huì)自動(dòng)的引用一個(gè)內(nèi)置模塊,叫做__builein__,這是python的一個(gè)標(biāo)準(zhǔn)庫模塊,直接import進(jìn)來,可以用dir(__builein__)看一下,里面都是預(yù)定義的一些變量名 
是不是看到了很多熟悉的面孔(type,sum.sorted,open),對(duì)的那些就是內(nèi)置的變量名,前面3種方法都找不到了就會(huì)去內(nèi)置作用域這個(gè)列表里面. 換句話時(shí)候若你本地變量有一個(gè)跟內(nèi)置變量一樣的,就會(huì)被本地變量覆蓋 def hider(): sum='newsum' return sum([1,2,3])
print hider() >>TypeError: 'str' object is not callable 就是因?yàn)長EGB法則,本地的sum變量把內(nèi)置作用域的sum變量覆蓋了 下面總結(jié)一下: 開頭的例子的答案,其實(shí)X的值不是88也不是77,而是都有可能,因?yàn)椴淮_定你先調(diào)用那個(gè)函數(shù),變量的值取決于函數(shù)調(diào)用的順序,而函數(shù)自身是任意順序進(jìn)行排列的,所以88,77都有可能,看哪個(gè)函數(shù)最后調(diào)用. 這會(huì)導(dǎo)致很難debug,你必須要跟蹤整個(gè)程序的控制流程,這其實(shí)就引出了另外一個(gè)話題,全局變量有相關(guān)性,用全局變量來記憶狀態(tài)信息太復(fù)雜,最后是通過面向?qū)ο蟮姆椒ǎ妙愡M(jìn)行封裝.
|