傳遞參數(shù)
函數(shù)傳遞參數(shù)時的一些簡要的關(guān)鍵點(diǎn):
- 參數(shù)的傳遞是通過自動將對象賦值給本地變量名來實現(xiàn)的。所有的參數(shù)實際上都是通過指針進(jìn)行傳遞的,作為參數(shù)被傳遞的對象從來不自動拷貝。
- 在函數(shù)內(nèi)部的參數(shù)名的賦值不會影響調(diào)用者。
- 改變函數(shù)的可變對象參數(shù)的值會對調(diào)用者有影響。
實際上,Python的參數(shù)傳遞模型和C語言的相當(dāng)相似:
不可變參數(shù)”通過值”進(jìn)行傳遞。像整數(shù)和字符串這樣的對象是通過對象引用而不是拷貝進(jìn)行的,但是因為不論怎么樣都不可能在原處改變不可變對象,實際的效果就很像創(chuàng)建了一份拷貝。可變對象是通過”指針”進(jìn)行傳遞的。這就意味著,可變對象能夠在函數(shù)內(nèi)部進(jìn)行原處修改。>>避免可變參數(shù)的修改避免參數(shù)的修改有很多種方式:
傳遞參數(shù)時,傳遞一個拷貝:
L=[1,2]changer(L[:])函數(shù)內(nèi)部進(jìn)行拷貝
defchanger(b):b=b[:]將可變對象轉(zhuǎn)化為不可變對象
L=[1,2]changer(tuple(L))>>對參數(shù)輸出進(jìn)行模擬對于參數(shù)的返回值有一個小技巧:因為return能夠返回任意種類的對象,如果這些值封裝進(jìn)一個元組或其他的集合類型,那么它也能夠返回多個值。
defmultiple(x,y):x=2y=[2,4]returnx,y#Returnnewvaluesinatuple這段代碼貌似返回了兩個值,其實只有一個:一個包含了2個元素的元組,它的括號是可以省略的。
特定的參數(shù)匹配模型
>>基礎(chǔ)知識匹配模型的大綱:
- 位置:從左至右進(jìn)行匹配。
- 關(guān)鍵字參數(shù):通過參數(shù)名進(jìn)行匹配。(調(diào)用者可以定義哪一個函數(shù)接受這個值,通過在調(diào)用時使用參數(shù)的變量名,使用name=value這種語法。)
- 默認(rèn)參數(shù):為沒有傳入值的參數(shù)定義參數(shù)值。
- 可變參數(shù):搜集任意多基于位置或關(guān)鍵字的參數(shù)。
- 可變參數(shù)解包:傳遞任意多的基于位置或關(guān)鍵字的參數(shù)。
- Keyword-only參數(shù):參數(shù)必須按照名稱傳遞。(只存在于Python3.0中)
>>匹配語法
語法位置解釋func(value)調(diào)用者常規(guī)參數(shù):通過位置進(jìn)行匹配。func(name=value)調(diào)用者關(guān)鍵字參數(shù):通過變量名匹配。func(*sequence)調(diào)用者以name傳遞所有的對象,并作為獨(dú)立的基于位置的參數(shù)。func(**dict)調(diào)用者以name成對的傳遞所有的關(guān)鍵字/值,并作為獨(dú)立的關(guān)鍵字參數(shù)。deffunc(name)函數(shù)常規(guī)參數(shù):通過位置或變量名進(jìn)行匹配。deffunc(name=value)函數(shù)默認(rèn)參數(shù)值,如果在調(diào)用中傳遞的話。deffunc(*name)函數(shù)匹配并收集(在元組中)所有包含位置的參數(shù)。deffunc(**name)函數(shù)匹配并收集(在字典中)所有包含位置的參數(shù)。deffunc(*args,name)函數(shù)參數(shù)必須在調(diào)用中按照關(guān)鍵字傳遞。deffunc(*,name=value)函數(shù)參數(shù)必須在調(diào)用中按照關(guān)鍵字傳遞。(Python3.0)相應(yīng)的說明:
在函數(shù)的調(diào)用中(表中的前4行),簡單的通過變量名位置進(jìn)行匹配,但是使用name=value的形式告訴Python依照變量名進(jìn)行匹配,這些叫做關(guān)鍵字參數(shù)。在調(diào)用中使用*sequence或**dict允許我們在一個序列或字典中相應(yīng)地封裝任意多的位置相關(guān)或者關(guān)鍵字的對象,并且在將他們傳遞給函數(shù)的時候,將它們解包為分開的、單個的參數(shù)。在函數(shù)的頭部,一個簡單的變量名時通過位置或變量名進(jìn)行匹配的(取決于調(diào)用者是如何傳遞給它參數(shù)的),但是name=value的形式定義了默認(rèn)的參數(shù)值。*name的形式收集了任意的額外不匹配的參數(shù)到元組中,并且**name的形式將會手機(jī)額外的關(guān)鍵字參數(shù)到字典中。在Python3.0及其以后的版本中,跟在*name或一個單獨(dú)的*之后的、任何正式的或默認(rèn)的參數(shù)名稱,都是keyword-only參數(shù),并且必須在調(diào)用時按照關(guān)鍵字傳遞。>>細(xì)節(jié)在使用混合的參數(shù)模型的時候,Python將會遵循下面有關(guān)順序的法則。
在函數(shù)調(diào)用中,參數(shù)必須以此順序出現(xiàn):任何位置參數(shù)(value),后面跟著任何關(guān)鍵字參數(shù)(name=value)和*sequence形式的組合,后面跟著**dict形式。在函數(shù)頭部,參數(shù)必須以此順序出現(xiàn):任何一般參數(shù)(name),緊跟著任何默認(rèn)參數(shù)(name=value),后面是name(在Python3.0中是)形式,后面跟著任何name或name=valuekeyword-only參數(shù)(Python3.0中),后面跟著**name形式。在調(diào)用和函數(shù)頭部中,如果出現(xiàn)**arg形式的話,都必須出現(xiàn)在最后。
Python內(nèi)部是使用以下的步驟來在賦值前進(jìn)行參數(shù)匹配的:
- 通過位置分配非關(guān)鍵字參數(shù)。
- 通過匹配變量名分配關(guān)鍵字參數(shù)。
- 其他額外的非關(guān)鍵字分配到*name元組中。
- 其他額外的關(guān)鍵字參數(shù)分配到**name字典中。
- 用默認(rèn)值分配給在頭部未得到分配的參數(shù)。
- 在這之后,Python會進(jìn)行檢測,確保每個參數(shù)只傳入了一個值。如果不是這樣的話,將會發(fā)生錯誤。當(dāng)所有匹配都完成了,Python把傳遞給參數(shù)名的對象賦值給它們。
>>關(guān)鍵字參數(shù)和默認(rèn)參數(shù)的實例如果沒有使用任何特殊的匹配語法,Python默認(rèn)會通過位置從左至右匹配變量名。
deff(a,b,c):print(a,b,c)f(1,2,3)#Prints1,2,3關(guān)鍵字參數(shù)
關(guān)鍵字參數(shù)允許通過變量名進(jìn)行匹配,而不是通過位置。
f(c=3,b=2,a=1)#Prints1,2,3默認(rèn)參數(shù)
默認(rèn)參數(shù)允許創(chuàng)建函數(shù)可選的參數(shù)。如果沒有傳入值的話,在函數(shù)運(yùn)行前,參數(shù)就被賦了默認(rèn)值。
deff(a,b=2,c=3):print(a,b,c)f(1)#Prints1,2,3f(1,4)#Prints1,4,3f(1,c=6)#Prints1,2,6關(guān)鍵字參數(shù)和默認(rèn)參數(shù)的混合
deffunc(spam,eggs,totast=0,ham=0):print((spam,eggs,totast=0,ham=0))func(1,2)#Ouput:(1,2,0,0)func(1,ham=1,eggs=0)#Ouput:(1,0,0,1)func(spam=1,eggs=0)#Ouput:(1,0,0,0)func(toast=1,eggs=2,spam=3)#Ouput:(3,2,1,0)func(1,2,3,4)#Ouput:(1,2,3,4)>>任意參數(shù)的實例最后兩種匹配擴(kuò)展,*和**,是讓函數(shù)支持接收任意數(shù)目的參數(shù)的。
收集參數(shù)
在函數(shù)定義中,在元組中收集不匹配的位置參數(shù)。
deff(*args):print(args)當(dāng)這個函數(shù)調(diào)用時,Python將所有位置相關(guān)的參數(shù)收集到一個新的元組中,并將這個元組賦值給變量args。因此它是一個一般的元組對象,能夠進(jìn)行索引或迭代。
**特性類似,但是它只對關(guān)鍵字參數(shù)有效。將這些關(guān)鍵字參數(shù)傳遞給一個新的字典,這個字典之后將能夠通過一般的字典工具進(jìn)行處理。在這種情況下,**允許將關(guān)鍵字參數(shù)轉(zhuǎn)化為字典,你能夠在之后使用鍵調(diào)用進(jìn)行步進(jìn)或字典迭代。
deff(a,*pargs,**kargs):print(a,pargs,kargs)f(1,2,3,x=1,y=2)#Prints:1(2,3){'x':2,'y':1}解包參數(shù)
在最新的Python版本中,我們在調(diào)用函數(shù)時能夠使用*語法。在這種情況下,它與函數(shù)定義的意思相反。它會解包參數(shù)的集合,而不是創(chuàng)建參數(shù)的集合。
deffunc(a,b,c,d):print(a,b,c,d)args=(1,2)args+=(3,4)func(*args)#Prints1,2,3,4相似的,在函數(shù)調(diào)用時,**會以鍵/值對的形式解包一個字典,使其成為獨(dú)立的關(guān)鍵字參數(shù)。
args={'a':1,'b':2,'c':3}args['d']=4func(**args)#Prints1,2,3,4注意:別混淆函數(shù)頭部和函數(shù)調(diào)用時*/**的語法:在頭部,它意味著收集任意多的參數(shù),而在調(diào)用時,它解包任意數(shù)量的參數(shù)。
應(yīng)用函數(shù)通用性
if<test>:action,args=func1,(1,)else:action,args=func2,(1,2,3)...action(*args)>>Python3.0Keyword-Only參數(shù)Python3.0把函數(shù)頭部的排序規(guī)則通用化了,允許我們指定keyword-only參數(shù)——即必須只按照關(guān)鍵字傳遞并且不會由一個位置參數(shù)來填充的參數(shù)。
從語法上講,keyword-only參數(shù)編碼為命名的參數(shù),出現(xiàn)在參數(shù)列表中的*args之后。所有這些參數(shù)都必須在調(diào)用中使用關(guān)鍵字語法來傳遞。
我們也可以在參數(shù)列表中使用一個*字符,來表示一個函數(shù)不會接受一個變量長度的參數(shù)列表,而是仍然期待跟在*后面的所有參數(shù)都作為關(guān)鍵字傳遞。
defkwonly(a,*,b,c):print(a,b,c)kwonly(1,c=3,b=2)#Prints:1,2,3kwonly(c=3,b=2,a=1)#Prints:1,2,3kwonly(1,2,3)#Error!上述代碼中,b和c必須按照關(guān)鍵字傳遞,不允許其他額外的位置傳遞。
另外,默認(rèn)函數(shù)仍然對keyword-only參數(shù)有效,所以,實際上,帶有默認(rèn)值的keyword-only參數(shù)都是可選的,但是,那些沒有默認(rèn)值的keyword-only參數(shù)真正地變成了函數(shù)必需的keyword-only參數(shù)。
排序規(guī)則最后,注意keyword-only參數(shù)必須在一個單個星號后指定,而不是兩個星號——命名的參數(shù)不能出現(xiàn)在**args任意關(guān)鍵字形式的后面,并且一個**不能獨(dú)自出現(xiàn)在參數(shù)列表中。這兩種做法將產(chǎn)生錯誤。
defkwonly(a,**pargs,b,c)#Error!defkwonly(a,**,b,c)#Error!這就意味著,在一個函數(shù)的頭部,keyword-only參數(shù)必須編寫在**args任意關(guān)鍵字形式之前,且在*args任意位置形式之后。
實際上,在函數(shù)調(diào)用中,類似的排序規(guī)則也是成立的:當(dāng)傳遞keyword-only參數(shù)的時候,它們必須出現(xiàn)在一個**args形式之前。keyword-only參數(shù)可以編寫在*arg之前或者之后,并且可能包含在**args中:
deff(a,*b,c=6,**d):print(a,b,c,d)f(1,*(2,3),**dict(x=4,y=5))#Prints:1(2,3)6{'x':4,'y':5}f(1,*(2,3),**dict(x=4,y=5),c=7)#Error!f(1,*(2,3),c=7,**dict(x=4,y=5))#Prints:1(2,3)7{'x':4,'y':5}f(1,c=7,*(2,3),**dict(x=4,y=5))#Prints:1(2,3)7{'x':4,'y':5}f(1,*(2,3),**dict(x=4,y=5,c=7))#Prints:1(2,3)7{'x':4,'y':5}Python作用域
在一個Python程序只用變量名時,Python創(chuàng)建、改變或查找變量名都是在所謂的命名空間(一個保存變量名的地方)中進(jìn)行的。也就是說,在代碼中變量名被賦值的位置決定了這個變量名能被訪問到的范圍,也即決定了它存在于哪個命名空間中。
除了打包程序之外,函數(shù)還為程序增加了一個額外的命名空間層:默認(rèn)情況下,一個函數(shù)所有變量名都是與函數(shù)的命名空間相關(guān)聯(lián)的。這意味著:
一個在def內(nèi)的定義的變量能夠在def內(nèi)的代碼使用,不能在函數(shù)的外部應(yīng)用這樣的變量名。def之中的變量名與def之外的變量名并不沖突,一個在def之外被賦值的變量X與在這個def之中賦值的變量X是完全不同的變量。>>作用域法則在開始編寫函數(shù)之前,我們編寫的所有代碼都是位于一個模塊的頂層(也就是說,并不是嵌套在def之中),所以我們使用的變量名要么是存在于模塊文件本身,要么就是Python內(nèi)置預(yù)先定義好的。函數(shù)定義本地作用域,而模塊定義的全局作用域。這兩個作用域有如下關(guān)系:
內(nèi)嵌的模塊是全局作用域每個模塊都是一個全局作用域(也就是說,一個創(chuàng)建于模塊文件頂層的變量的命名空間)。對于模塊外部來說,該模塊的全局變量就成為了這個模塊對象的屬性,但是在這個模塊中能夠像簡單的變量一樣使用。全局作用域的作用范圍僅限于單個文件這里的全局指的是在一個文件的頂層的變量名僅對于這個文件內(nèi)部的代碼而言是全局的。在Python中是沒有基于一個單個的、無所不包的情景文件的全局作用域的。每次對函數(shù)的調(diào)用都創(chuàng)建了一個新的本地作用域賦值的變量名除非聲明為全局變量或非局部變量,否則均為局部變量所有的變量名都可以歸納為本地、全局或者內(nèi)置的>>變量名解析:LEGB原則Python的變量名解析機(jī)制有時稱為LEGB法則,當(dāng)在函數(shù)中使用未認(rèn)證的變量名時,Python搜索4個作用域:
- 本地作用域(L)
- 上一層結(jié)構(gòu)中def或lambda的本地作用域(E)(其實就是函數(shù)嵌套的情況)
- 全局作用域(G)
- 最后是內(nèi)置作用域(B)
Python按順序在上面4個作用域中查找變量,并且在第一個能夠找到這個變量名的地方停下來,如果在這4個作用域中都沒找到,Python會報錯。
這里需要強(qiáng)調(diào)的是,上面四個作用域是函數(shù)中代碼的搜索過程,也就是說,在函數(shù)中能直接使用上一層中的變量!
s=10deftimes(x,y):x=sreturnx*ytimes(3,4)#return40not12>>內(nèi)置作用域內(nèi)置作用域是通過一個名為builtin的標(biāo)準(zhǔn)模塊來實現(xiàn)的,但是這個變量名自身并沒有放入內(nèi)置作用域內(nèi),所以必須導(dǎo)入這個文件才能夠使用它。在Python3.0中,可以使用以下的代碼來查看到底預(yù)定義了哪些變量:
importbuiltinsdir(builtins)因此,事實上有兩種方法可以引用一個內(nèi)置函數(shù):通過LEGB法則帶來的好處,或者手動導(dǎo)入builtin模塊。其中第二種方法在一些復(fù)雜的任務(wù)里是很有用的,因為一些局部變量有可能會覆蓋內(nèi)置的變量或函數(shù)。再次強(qiáng)調(diào)的是,LEGB法則只使它找到的第一處變量名的地方生效!
global語句
global語句是一個命名空間的聲明,它告訴Python解釋器打算生成一個或多個全局變量,也就是說,存在于整個模塊內(nèi)部作用域(命名空間)的變量名。關(guān)于全局變量名:
全局變量是位于模塊文件內(nèi)部頂層的變量名。全局變量如果是在函數(shù)內(nèi)部被賦值的話,必須經(jīng)過聲明。全局變量名在函數(shù)的內(nèi)部不經(jīng)過聲明也可以被引用。global語句包含了關(guān)鍵字global,其后跟著一個或多個由逗號分開的變量名。當(dāng)在函數(shù)主題被賦值或引用時,所有列出來的變量名將被映射到整個模塊的作用域內(nèi)。舉個例子:
X=88deffunc():globalXX=99func()print(X)#Prints99作用域和嵌套函數(shù)
這部分內(nèi)容是關(guān)于LEGB查找法則中E這一層的,它包括了任意嵌套函數(shù)內(nèi)部的本地作用域。嵌套作用域有時也叫做靜態(tài)嵌套作用域。實際上,嵌套是一個語法上嵌套的作用域,它是對應(yīng)于程序源代碼的物理結(jié)構(gòu)上的嵌套結(jié)構(gòu)。
>>嵌套作用域的細(xì)節(jié)對于一個函數(shù)來說:
一個引用(X)首先在本地(函數(shù)內(nèi))作用域查找變量名X;之后會在代碼的語法上嵌套了的函數(shù)中的本地作用域,從內(nèi)到外查找;之后查找當(dāng)前的全局作用域(模塊文件);最后在內(nèi)置作用域內(nèi)(模塊builtin)。全局聲明將會直接從全局(模塊文件)作用域進(jìn)行搜索。其實就是從引用X的地方開始,一層一層網(wǎng)上搜索,直到找到的第一個X。在默認(rèn)情況下,一個賦值(X=value)創(chuàng)建或修改了變量名X的當(dāng)前作用域。如果X在函數(shù)內(nèi)部聲明為全局變量,它將會創(chuàng)建或改變變量名X為整個模塊的作用域。另一方面,如果X在函數(shù)內(nèi)部聲明為nonlocal,賦值會修改最近的嵌套函數(shù)的本地作用域中的名稱X。>>嵌套作用域舉例
X=99deff1():X=88deff2():print(X)f2()f1()#Prints88:enclosingdeflocal首先需要說明的是,上面這段代碼是合法的,def是一個簡單的執(zhí)行語句,可以出現(xiàn)在任意其他語句能夠出現(xiàn)的地方,包括嵌套在另一個def之中。代碼中,f2是在f1中定義的函數(shù),在此情況下,f2是一個臨時函數(shù),僅在f1內(nèi)部執(zhí)行的過程中存在(并且只對f1中的代碼可見)。通過LEGB查找法則,f2內(nèi)的X自動映射到了f1的X。
值得注意的是,這個嵌套作用域查找在嵌套的函數(shù)已經(jīng)返回后也是有效的。
X=99deff1():X=88deff2():print(X)#RememberXinenclosingdefscopereturnf2#Returnf2butdon'tcallitaction=f1()#Makereturnfunctionaction()#Callitnow:Prints88上述代碼中,不管調(diào)用幾次action函數(shù),返回值都是88,f2記住了f1中嵌套作用域中的X,盡管此時f1已經(jīng)不處于激活的狀態(tài)。
工廠函數(shù)
上述這些行為有時叫做閉合(closure)或者工廠函數(shù)——一個能夠記住嵌套作用域的變量值的函數(shù),即使那個作用域也許已經(jīng)不存在了。通常來說,使用類來記錄狀態(tài)信息時更好的選擇,但是像這樣的工廠函數(shù)也提供了一種替代方案。具體的例子:
defmaker(N):defaction(X):returnX**Nreturnactionf=maker(2)#Pass2toNf(3)#Pass3toX,Nremembers2:3**2,Return9f(4)#return4**2g=maker(3)#gremembers3,fremembers2g(3)#return27f(3)#return9從上面代碼中可以看到,f和g函數(shù)分別記錄了不同的N值,也就是記錄了不同的狀態(tài),每一次對這個工廠函數(shù)進(jìn)行賦值,都會得到一個狀態(tài)信息的集合,每個函數(shù)都有自己的狀態(tài)信息,由maker中的變量N保持。
作用域與帶有循環(huán)變量的默認(rèn)參數(shù)相比較
在已給出的法則中有一個值得注意的特例:如果lambda或者def在函數(shù)中定義,嵌套在一個循環(huán)之中,并且嵌套的函數(shù)引用了一個上層作用域的變量,該變量被循環(huán)所改變,所有在這個循環(huán)中產(chǎn)生的函數(shù)都將會有相同的值——在最后一次循環(huán)中完成時被引用變量的值。具體的例子:
defmakeActions():acts=[]foriinrange(5):#Triestoremembereachiacts.append(lambdax:i**x)#Allremembersamelastitreturnacts盡管是在嘗試創(chuàng)建一個函數(shù)列表,使得每個函數(shù)擁有不同的狀態(tài)值,但是事實上,這個列表中的函數(shù)的狀態(tài)值都是一樣的,是4。因為嵌套作用域中的變量在嵌套的函數(shù)被調(diào)用時才進(jìn)行查找,所以它們實際上記住的是同樣的值(在最后一次循環(huán)迭代中循環(huán)變量的值)。
為了能讓這類代碼能夠工作,必須使用默認(rèn)參數(shù)把當(dāng)前的值傳遞給嵌套作用域的變量。因為默認(rèn)參數(shù)是在嵌套函數(shù)創(chuàng)建時評估的(而不是在其稍后調(diào)用時),每一個函數(shù)記住了自己的變量i的值。
defmakeActions():acts=[]foriinrange(5):#Usedefaultinsteadacts.append(lambdax,i=i:i**x)#Remembercurrentireturnacts{nonlocal語句
事實上,在Python3.0中,我們也可以修改嵌套作用域變量,只要我們在一條nonlocal語句中聲明它們。使用這條語句,嵌套的def可以對嵌套函數(shù)中的名稱進(jìn)行讀取和寫入訪問。nonlocal應(yīng)用于一個嵌套的函數(shù)的作用域中的一個名稱,而不是所有def之外的全局模塊作用域——它們可能只存在于一個嵌套的函數(shù)中,并且不能由一個嵌套的def中第一次賦值創(chuàng)建。
換句話說,nonlocal即允許對嵌套的函數(shù)作用域中的名稱變量賦值,并且把這樣的名稱作用域查找限制在嵌套的def。
>>nonlocal基礎(chǔ)
deffunc():nonlocalname1,name2...這條語句允許一個嵌套函數(shù)來修改在一個語法嵌套函數(shù)的作用域中定義的一個或多個名稱。在Python2.X中,當(dāng)一個函數(shù)def嵌套在另一個函數(shù)中,嵌套的函數(shù)可以引用上一層函數(shù)中定義的各種變量,但是不能修改它們。在Python3.0中,在一條nonlocal語句中聲明嵌套的作用域,使得嵌套的函數(shù)能夠賦值,并且由此也能夠修改這樣的名稱。
除了允許修改嵌套的def中的名稱,nonlocal語句還加快了引用——就像global語句一樣,nonlocal使得對該語句中列出的名稱的查找從嵌套的def的作用域中開始,而不是從聲明函數(shù)的本地作用域開始,也就是說,nonlocal也意味著”完全略過我的本地作用域”。
實際上,當(dāng)執(zhí)行到nonlocal語句的時候,nonlocal中列出的名稱必須在一個嵌套的def中提前定義過,否則,將會產(chǎn)生一個錯誤。直接效果和global很相似:global意味著名稱位于上一層的模塊中,nonlocal意味著它們位于一個上一層的def函數(shù)中。nonlocal甚至更加嚴(yán)格——作用域查找只限定在嵌套的def。也就是說,nonlocal只能出現(xiàn)在嵌套的def中,而不能在模塊的全局作用域中或def之外的內(nèi)置作用域中。
當(dāng)在一個函數(shù)中使用的時候,global和nonlocal語句都在某種程度上限制了查找規(guī)則:
global使得作用域查找從嵌套的模塊的作用域開始,并且允許對那里的名稱賦值。如果名稱不存在與該模塊中,作用域查找繼續(xù)到內(nèi)置作用域,但是,對全局名稱的賦值總是在模塊作用域中創(chuàng)建或修改它們。nonlocal限制作用域查找只是嵌套的def,要求名稱已經(jīng)存在于那里,并且允許對它們賦值。作用域查找不會繼續(xù)到全局或內(nèi)置作用域。>>nonlocal應(yīng)用使用nonlocal進(jìn)行修改
deftester(start):state=start#eachcallgetsitsownstatedefnested(label):nonlocalstate#rememberstateinenclosingscopeprint(label,state)state+=1#AllowedtochangeitifonolocalreturnnestedF=tester(0)#IncrementsstateoneachcallF('spam')#Prints:spam0F('ham')#Prints:ham1F('eggs')#Prints:eggs2邊界情況
當(dāng)執(zhí)行一條nonlocal語句時,nonlocal名稱必須已經(jīng)在一個嵌套的def作用域中賦值過,否則將會得到一個錯誤。nonlocal限制作用域查找僅為嵌套的def,nonlocal不會在嵌套的模塊的全局作用域或所有def之外的內(nèi)置作用域中查找。