做用域是指變量的生效範圍,例如本地變量、全局變量描述的就是不一樣的生效範圍。html
python的變量做用域的規則很是簡單,能夠說是全部語言中最直觀、最容易理解的做用域。python
在開始介紹做用域以前,先拋一個問題:安全
x=1 def f(): x=3 g() print("f:",x) # 3 def g(): print("g:",x) # 1 f() print("main:",x) # 1
上面的代碼將輸出三、一、1。解釋參見再述做用域規則。另外,我的建議,本文最後一小節內容儘可能理解透徹。多線程
它有4個層次的做用域範圍:內部嵌套函數、包含內部嵌套函數的函數自身、全局做用域、內置做用域。上面4個做用域的範圍排序是按照從內到外,從小到大排序的。閉包
其中:app
__builtins__
模塊中。這些名稱主要是一些關鍵字,例如open、range、quit等因此對於下面這段python代碼來講,若是它處於a.py文件中,且沒有嵌套在其它函數內:函數
X=1 def out1(i): X=2 Y='a' print(X) print(i) def in1(n): print(n) print(X,Y) in1(3) out1(2)
那麼:
處於全局做用域範圍的變量有:X、out1
處於out1本地做用域範圍的變量有:i、X、Y、in1
處於嵌套在函數out1內部的函數in1的本地做用域範圍的變量有:n工具
注意上面的函數名out1和in1也是一種變量。測試
以下圖所示:優化
當在某個範圍引用某個變量的時候,將從它所在的層次開始搜索變量是否存在,不存在則向外層繼續搜索。搜索到了,則當即中止。
例如函數ab()中嵌套了一個函數cd(),cd()中有一個語句print(x)
,它將首先檢查cd()函數的本地做用域內是否有x,若是沒有則繼續檢查外部函數ab()的本地做用域範圍內是否有x,若是沒有則再次向外搜索全局範圍內的變量x,若是仍是沒有,則繼續搜索內置做用域,像"x"這種變量名,在內置做用域範圍內是不存在的,因此最終沒有搜索到,報錯。若是一開始在cd()中就已經找到了變量x,就不會再搜索ab()範圍以及更外層的範圍。
因此,內層範圍能夠引用外層範圍的變量,外層範圍不包括內層範圍的變量。
內置做用域主要是一些內置的函數名、內置的異常等關鍵字。例如open,range,quit等。
兩種方式能夠搜索內置做用域:一是直接導入builtins模塊,二是讓python自動搜索。導入builtins模塊會讓內置做用域內的變量直接置於當前文件的全局範圍,自動搜索內置做用域則是最後的階段進行搜索。
通常來講無需手動導入builtins模塊,不過能夠看看這個模塊中包含了哪些內置變量。
>>> import builtins >>> dir(builtins) ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', ............... 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
若是在函數內部引用了一個和全局變量同名的變量,且不是從新定義、從新賦值(其實python中沒有變量聲明的概念,只有賦值的概念),那麼函數內部引用的是全局變量。
例如,下面的函數g()中,print函數中的變量x並未在g()中單獨定義或賦值,因此這個x引用的是全局變量x,它將輸出值3。
x=3 def g(): print(x) # 引用全局變量x
若是函數內部從新賦值了一個和全局變量名稱相同的變量,則這個變量是本地變量,它會掩蓋全局變量。注意是掩蓋而非覆蓋,掩蓋的意思是出了函數的範圍(函數退出),全局變量就會恢復。或者換句話說,在函數內部看到的是本地變量x=2
,在函數外部看到的是全局變量x=3
。
例如:下面的g()中從新聲明瞭x,這個x稱爲g()的本地變量,全局變量x=3
暫時被掩蓋(固然,這是對該函數來講的掩蓋)。
x=3 def g(): x=2 # 定義並賦值本地變量x print(x) # 引用本地變量x
python是一種解釋性語言,讀一行解釋一行,讀了下一行就忘記前一行(詳細見下文)。因此在使用變量以前必須先進行變量的定義(聲明)。
例以下面是錯誤的:
def g(): print(x) x=3 g()
錯誤信息:
UnboundLocalError: local variable 'x' referenced before assignment
這個很好理解,可是下面和同名的全局變量混合的時候,就不那麼容易理解了:
x=1 def g(): print(x) x=2 g()
這裏也會報錯,而不是輸出x=1或2。
這裏須要解釋一下,雖然說python是逐行解釋的。但每一個函數屬於一個區塊,這個區塊範圍是一次性解釋的,並不會讀一行忘記一行,而是一直讀,讀完整個區塊再解釋。因此,讀完整個g()區塊後,首先就記住了從新定義了本地變量x=2
,因而g()中全部使用變量x的時候,都是本地變量x,因此print(x)中的x也是本地變量,但這違反了使用變量前先賦值的規則,因此也會報錯。
所以,在函數內修改和全局變量同名的變量前,必須先修改,再使用該變量。因此,上面的代碼中,x=2
必須放在print的前面:
x=1 def g(): x=2 print(x) g()
因此,對於函數來講,也必須先定義函數,再調用函數。下面是錯誤的:
g() def g(): x=2 print(x)
報錯信息:
NameError: name 'g' is not defined
可是下面的代碼中,f()中先調用了g(),而後才定義g(),爲何能執行呢:
x=1 def f(): x=3 g() print("f:",x) # 3 def g(): print("g:",x) # 1 f() print("main:",x) # 1
實際上並不是是先調用了g(),python解釋到def f()區塊的時候,只是聲明這一個函數,並不是調用這個函數,真正調用f()的時候是在def g()
區塊的後面,因此其實是先聲明完f()和g()以後,再調用f()和g()的。
但若是把f()放在def f()
和def g()
的中間,將會報錯,由於調用f()函數的時候,def g()
還沒解釋到,也就是說g()尚未聲明。
x=1 def f(): x=3 g() print("f:",x) f() # 報錯 def g(): print("g:",x)
更容易犯錯的一種狀況是邊賦值,邊使用。下面的代碼是錯誤的:
x=3 def f1(): x += 3 print(x) f1()
由於x += 3
也是賦值操做,函數內部只要是賦值操做就表示聲明爲本地變量。它首先計算x=x+3
右邊的x+3
,而後將結果賦值給左邊的變量x,但計算x+3
的時候變量x並未定義,因此它是錯誤的。錯誤信息:
UnboundLocalError: local variable 'x' referenced before assignment
關於python中的全局變量:
默認狀況下,下面f()中的x變量是全局變量:
x=2 def f(): print(x) f() # 輸出2
默認狀況下,下面f()中的x變量是本地變量:
x=2 def f(): x=3 print(x) f() # 輸出3 print(x) # 輸出2
若是想要在def的內部修改全局變量,就須要使用global關鍵字聲明變量:
x=2 def f(): global x x=3 print(x) f() # 輸出3 print(x) # 輸出3
global能夠聲明一個或多個變量爲全局變量,多個變量使用逗號隔開,也能夠聲明事先不存在的變量爲全局變量:
x=2 def f(): global x,y x,y = 3,4 print(x,y) f() print(x,y)
不能global中直接賦值。因此下面的是錯的:
global x=2
注意,global不是聲明變量,在變量賦值以前,變量是必定不存在的,就算是被global修飾了也同樣不存在,因此下面的代碼是錯的。實際上,global有點相似於聲明變量的名稱空間,而非變量。
x=2 def f(): global x,y print(y)
報錯信息:
NameError: name 'y' is not defined
必須在print(y)以前(不能是以後)加上y的賦值語句,才表示它的存在。
x=2 def f(): global x,y y=3 print(y)
global修飾的變量必須在它的賦值以前,因此下面的是錯的,由於y=2首先將它聲明爲本地變量了。
def f(): y=2 global y
考慮下面這個問題:
x=2 def f(): global x x=3 def g(): global x x=4 f()或g() print(x)
這時,函數f()和g()的調用順序決定了print輸出的全局變量x的值。由於全局變量是共享的,若是多線程同時執行這段代碼,就不知道是誰先誰後修改,致使print(x)的值隨機性。這是多線程不安全特性。所以,若是容許,應儘可能不要將函數內部的變量修飾爲全局變量。
python中一個文件一個模塊,在模塊1中能夠導入模塊2中的屬性(例如全局變量)。
例如,b.py文件中:
x=3
a.py文件中:
import b print(b.x) b.x=4
上面的a.py中導入了b.py模塊,經過b.x
能夠訪問、修改這個來自於b.py中的全局變量x。
這是極不安全的,由於誰也不知道是否有其餘的模塊也在修改b.x
。
因此,沒有人會去直接修改其它模塊的屬性,若是要修改,基本上都會經過相似面向對象中的setter函數進行修改。只需在b.py中定義一個函數,之後在其它文件中使用這個函數修改便可。
b.py文件中:
x=3 def setx(n) global x x=n
a.py文件中:
import b b.setx(54) # 將b.x變量設置爲54
上面經過import導入模塊文件,就能夠獲取這個模塊中屬性的訪問權。實際上,也能夠在當前模塊文件中使用import mod_name
導入當前模塊,其中mod_name
爲當前文件名,這樣就能夠在函數內部直接訪問全局變量,而無需使用global關鍵字。
除了import mod_name
能夠導入當前模塊,使用sys模塊的modules()函數也能夠導入:sys.modules['mod_name']
。
例如,在b.py文件中:
x=3 def f(): global x x += 2 def f1(): x=4 # 本地變量 def f2(): x=4 # 本地變量 import b b.x += 2 # 全局變量 def f3(): x=4 # 本地變量 import sys glob = sys.modules['b'] glob.x += 2 # 全局變量 def test(): print("aaa",x) # 輸出3 f();f1();f2();f3() print("bbb",x) # 輸出9
在a.py文件中:
import b b.test()
當函數進行嵌套的時候,內層函數的做用域是最內層的,它的外層是外層函數的做用域。內層函數和外層函數的關係相似於本地做用域與全局做用域的關係:
例如,下面的嵌套代碼中,f2()中print(x,y)
的x是屬於外層函數f1()的本地變量,而y則是屬於內層函數自身的本地變量,外層函數f1()沒法訪問屬於內層函數的y。
x=3 def f1(): x=4 def f2(): y=5 print(x,y) f2() f1()
nonlocal語句能夠修飾內層函數中的變量使其成爲它上一層函數的變量。它的用法和global基本相同,修飾多個變量的時候,須要逗號隔開。但和global有一點不一樣,global修飾的變量可能事先並未存在於全局做用域內,但nonlocal修飾的變量必須已經存在於上層或上上層(或更多層)函數,不能只存在於全局(見下面示例)。
例以下面的代碼片斷中嵌套了2次,其中f3()中的x使用nonlocal修飾,使得這個x變成它上一層做用域f2()中的x變量。
x=3 def f1(): x=4 # f1的本地變量 def f2(): x=5 # f2的本地變量 def f3(): nonlocal x # f2的本地變量 print("f3:",x) # 輸出5 x=6 f3() print("f2:",x) # 被修改,輸出6 f2() f1()
上面的代碼將輸出:
f3: 5 f2: 6
若是將上面的f2()中的x=5
刪除,會如何?
x=3 def f1(): x=4 def f2(): def f3(): nonlocal x # f1()的本地 print("f3:",x) # 輸出4 x=6 # 修改f1()的本地 f3() print("f2:",x) # 輸出6 f2() print("f1:",x) # 輸出6 f1()
注意,上面f3()中的nonlocal將x修飾爲f1()的本地變量,由於f3()的上一層f2()中沒有變量x,因此f2()繼承了f1()的變量x,使得f3()修改上一層f2()中的變量,等價於修改f1()中的變量x。
但若是把f1()中的x=4
也刪除,那麼將報錯,由於nonlocal沒法將變量修飾爲全局範圍。
因此,nonlocal默認將內層函數中的變量修飾爲上一層函數的做用域範圍,若是上一層函數中不存在該變量,則修飾爲上上層、上上上層直到頂層函數,但不能修飾爲全局做用域範圍。
一樣的,只要在內層函數中賦值,就表示聲明這個變量的做用域爲內層函數做用域範圍。因此,下面的代碼是錯誤的:
x=3 def f1(): x=4 def f2(): print(x) x=3 f2() f1()
下面的代碼也是錯的:
x=3 def f1(): x=4 def f2(): x += 3 print(x) f2() f1()
錯誤信息:
UnboundLocalError: local variable 'x' referenced before assignment
至於緣由,前文已經解釋的很清楚。
在之前的版本中,尚未nonlocal關鍵字,這時若是要保存外層函數的變量,就須要使用函數參數默認值的方式定義內層函數。
x=3 def f1(): x=4 def f2(x=x): x += 3 print("f2:",x) x=5 f2() print("f1:",x) f1()
輸出:
f2: 7 f1: 5
上面的f2(x=x)
中,等號右邊的x來自於f1()中x=4
,而後將其賦值給f2()的本地做用域變量x。注意,python的做用域是詞法做用域,函數區塊的定義位置決定了它看到的變量。因此,儘管調用f2()以前再次對x進行了賦值,f2()函數調用時,f2(x=x)
等號右邊的x早已經賦值給左邊的本地變量x了。它們的關係以下圖所示:
通常來講,函數嵌套都只用於閉包(工廠函數),並且是結合匿名函數(lambda)實現的閉包。其它時候,函數嵌套通常均可以改寫爲非嵌套模式。
例如,下面的嵌套函數:
def f1(): x=3 def f2(): nonlocal x print(x) f2() f1()
能夠改寫爲:
def f1(): x=3 f2(x) def f2(x): print(x) f1()
當函數位於循環結構中,且這個函數引用了循環控制變量,那麼結果可能會出乎意料。
原本以匿名函數(lambda)來解釋更清晰,但由於還沒有介紹匿名函數,因此這裏採用命名函數爲例。
下面的代碼中,將5個函數做爲列表的元素保存到列表list1中。
def f1(): list1 = [] for i in range(5): def n(x): return i+x list1.append(n) return list1 mylist = f1() for i in mylist: print(i) print(mylist[0](2)) print(mylist[2](2))
結果:
<function f1.<locals>.n at 0x02F93660> <function f1.<locals>.n at 0x02F934B0> <function f1.<locals>.n at 0x02F936A8> <function f1.<locals>.n at 0x02F93738> <function f1.<locals>.n at 0x02F93780> 6 6
從結果中能夠看到mylist[0](2)
和mylist[2](2)
的執行結果是同樣的,不只如此,mylist[N](2)
的結果也全都同樣。換句話說,保存到列表中的各個函數n()中所引用的循環控制變量"i"並無由於循環的迭代而改變,並且列表中全部函數保存的i的值都是循環的最後一個元素i=4
。
(注:對於此現象,各語言基本都是如此,本節稍做解釋,真正的本質緣由在本文的最後一節作了額外的補充解釋代碼塊細述)。
先看下面的例子:
def f1(): for i in range(5): def n(): print(i) return n f1()()
結果輸出4。可見,print(i)
的值並無隨循環的迭代過程而改變。
究其緣由,是由於def n()
只是函數的聲明,它不會去查找i的值是多少,因此不會將i的值替換到函數n()的i變量,而是直接保存變量i的地址,當循環結束時,i指向最後一個元素i=4的地址。
當開始調用n()的時候,即f1()()
,纔會真正開始查找i的值,這時候i指向的正是i=4。
這就像下面的代碼同樣,在尚未開始調用f()的時候,f()內部的x一直都只是指向它所看見的變量x,而這個x是全局做用域範圍。當真正開始調用f()的時候,纔會去定位x的指向。
x=3 def f(): print(x)
回到上面循環中的嵌套函數,若是要保證循環的迭代能做用到其內部的函數中,能夠採用默認參數值的方式進行賦值:
def f1(): list1 = [] for i in range(5): def n(x,i=i): return i+x list1.append(n) return list1
上面def n(x,i=i)
中的i=i
是設置默認參數值,等號右邊的i是函數聲明時就查找並替換完成的,因此每次循環迭代過程當中,等號右邊的i都不一樣,等號左邊的參數i的默認值就不一樣。
python的做用域是詞法做用域,這意味着函數的定義位置決定了它所看見的變量。除了詞法做用域,還有動態做用域,動態做用域意味着函數的調用位置決定了它所看見的變量。關於詞法、動態做用域,本文很少作解釋,想要了解的話,能夠參考一文搞懂:詞法做用域、動態做用域、回調函數、閉包
下面是本文開頭的問題:
x=1 def f(): x=3 g() print("f:",x) # 3 def g(): print("g:",x) # 1 f() print("main:",x) # 1
對於python的這段代碼來講,這裏有兩個值得注意的地方:
第一個問題在前文已經解釋過了,再解釋一遍:雖然f()裏面有g()的調用語句,但def f()
只是聲明,但在調用f()以前,是不會去調用g()的。因此,只要f()的調用語句在def g()
以後,就是正確的。
第二個問題,python是詞法做用域,因此:
def f()
,在此期間會建立一個本地變量x,而且print("f:",x)
中的x指向這個本地變量;g()
,在此期間,g()的定義語句不在f()內部,而是在全局範圍,因此它看見的是x是全局x,因此print("g:",x)
中的x指向全局變量x;當調用f()的時候,執行到g()時,g()中所指向的是全局範圍的x,而非f()段中的x。因此,輸出1。
再看一個嵌套在函數內部的示例:
x=3 def f1(): x=4 def f2(): print(x) x=5 f2() f1() # 輸出5
這裏的問題是f2()中的print爲何不輸出4,而是輸出5。
其實也很容易理解,由於def f2()
是定義在f1()內部的,因此f2()看見的x是f1()中的x,也就是說print(x)
中的x指向的是f1()中的x。但在調用f2()以前,從新賦值了x=5
,等到調用f2()的時候,根據x的指向,將找到新的x的值。
也就是說,前面的示例中,有兩個獨立的變量x:全局的和f()本地的。後面這個示例中只有一個變量x,屬於f()。
代碼塊可使得一段python代碼做爲一個單元、一個總體執行。如下是 官方手冊 的描述。
A Python program is constructed from code blocks. A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition. Each command typed interactively is a block. A script file (a file given as standard input to the interpreter or specified as a command line argument to the interpreter) is a code block. A script command (a command specified on the interpreter command line with the ‘-c’ option) is a code block. The string argument passed to the built-in functions eval() and exec() is a code block.
因此,有如下幾種類型的代碼塊:
代碼塊的做用是組織代碼,同時意味着退出代碼區塊範圍就退出了做用域範圍。例如退出函數區塊,就退出了函數的做用域,使得函數內的本地變量沒法被函數的外界訪問。
此外,python是解釋性語言,讀一行解釋一行,這意味着每讀一行就忘記前一行。但實際上更嚴格的說法是讀一個代碼塊解釋一個代碼塊,這意味着讀代碼塊中的內容時,是暫時記住屬於這個代碼塊中所讀內容的,讀完整個代碼塊後再以統籌的形式解釋這個代碼塊。
先說明讀一行解釋一行的狀況,也就是每一行都屬於一個代碼塊,這個只能經過python的交互式工具idle工具來測試:
>>> x=2000 >>> y=2000 >>> x is y False >>> x=2000;y=2000 >>> x is y True
理論上分號是語句的分隔符,並不會影響結果。但爲何第一個x is y
爲False,而第二個x is y
爲True?
首先分析第一個x is y
。因爲交互式工具idle中每個命令都是一個單獨的語句塊,這使得解釋完x=2000
後馬上就忘記了2000這個數值對象,同時忘記的還有x變量自己。而後再讀取解釋y=2000
,由於不記得剛纔解釋的x=2000
,因此會在內存中從新建立一個數值結構用來保存2000這個數值,而後用y指向它。換句話說,x和y所指向的2000在內存中是不一樣的數據對象,因此x is y
爲False。
下面的x is y
返回True:
>>> x=2000;y=2000 >>> x is y True
由於python按行解釋,一個命令是一個代碼塊。對於x=2000;y=2000
,python首先讀取這一整行,發現x和y的數值對象都是2000,因而作個簡單優化,等價於x,y=2000,2000
,這意味着它們屬於一個代碼塊內,因爲都是2000,因此只會在內存中建立一個數據對象,而後x和y都引用這個數據對象。因此,x is y
返回True。
idle工具中每一個命令都是獨立的代碼塊,可是py文件倒是一個完整的代碼塊,其內還能夠嵌套其它代碼塊(如函數、exec()等)。因此,若是上面的分行賦值語句放在py文件中,獲得的結果將是True。
例如:
x = 2000 y = 2000 print(x is y) # True def f1(): z=2000 z1=2000 print(x is z) # False print(z is z1) # True f1()
python先讀取x=2000
,並在內存中建立一個屬於全局做用域的2000數據對象,再解釋y=2000的時候,發現這個全局對象2000已經存在了(由於x和y同處於全局代碼塊內),因此不會再額外建立新的2000對象。這裏反映出來的結果是"同一個代碼塊內,雖然仍然是讀一行解釋一行,但在退出這個代碼塊以前,不會忘記這個代碼塊中的內容,並且會統籌安排這個代碼塊"。
同理def f1()
內的代碼塊,由於z是本地做用域的變量,更標準的是處於不一樣代碼塊內,因此會在本地做用域內存區建立新的數據對象2000,因此x is z
返回False。根據前面的解釋,z1 is z
返回True。
再回顧前文屢次出現的一個異常:
x = 3 def f1(): print(x) x=4 f1()
報錯信息:
UnboundLocalError: local variable 'x' referenced before assignment
當執行到def語句的時候,由於def聲明函數,函數體是一個代碼塊,因此按照代碼塊的方式讀取屬於這個代碼塊中的內容。首先讀取print(x)
,但並不會直接解釋,而是會記住它,並繼續向下讀取,因而讀取x=4,這意味着x是一個本地變量。而後統籌安排整個代碼塊,將print(x)的x認爲是本地變量而非全局變量。注意,直到def退出的時候都尚未進行x的賦值,而是記錄了本地變量x,賦值操做是在函數調用的時候進行的。當調用函數f()的時候,發現print(x)中的x是本地變量,但由於尚未賦值,因此報錯。
可是再看下面的,爲何又返回True?
>>> x=256 >>> y=256 >>> x is y True
由於Python在啓動的時候就在內存中預先爲經常使用的較小整數值(-5到256)建立好了對象,由於它們使用的很是頻繁(有些在python的內部已經使用了)。因此,對於這個範圍內的整數,都是直接引用,不會再在內存中額外建立新的數值對象,因此x is y
老是返回true。甚至,這些小值整數能夠跨做用域:
x = 3 def f1(): y=3 print(x is y) # True f1()
再看前文循環內的函數的問題。
def f1(): for i in range(5): def n(): print(i) return n f1()()
前面對現象已經解釋過,內部函數n()中print(i)的i不會隨循環的迭代而改變,而是固定的值i=4。
python首先解釋def f1()
的代碼塊,會記錄屬於這個代碼塊做用域內的變量i和n,但i和n都不會賦值,也就是說暫時並不知道變量n是一個函數變量。
同理,當須要解釋def n()
代碼塊的時候,將記住這個代碼塊涉及到的變量i,只不過這個變量i是屬於外層函數的,但無論如何,這個代碼塊記住了i,且記住了它是外部函數做用域的。
注意,函數的聲明過程當中,全部涉及到變量i的做用域內都不會對i進行賦值,僅僅只是保存了這個i變量名,只有在調用函數的時候纔會進行賦值操做。
當開始調用f1()的時候,開始執行函數體中的代碼,因而開始循環迭代,且屢次聲明函數n()
,每一次迭代生成的n()都會讓原先已記錄的變量n指向這個新聲明的函數體(至關於賦值的操做,只不過是變量n引用的對象是函數體結構,而不是通常的數據對象),因爲只是在循環中聲明函數n(),並無進行調用,因此不會對n()中的i進行賦值操做。並且,每次循環迭代都會讓變量n指向新的函數體,使得先前迭代過程當中定義的函數被丟棄(覆蓋),因此最終只記住了最後一輪循環時聲明的函數n(),而且i=4。
當調用f1()()時,表示調用f1()中返回的函數n(),直到這個時候纔會對n()內的i進行賦值,賦值時將搜索它的外層函數f1()做用域,發現這個做用域內的i指向內存中的數值4,因而最終輸出4。
再看下面的代碼:
def f1(): for i in range(5): def n(): print(i) n() return n f1()
輸出結果:
0 1 2 3 4
調用f1()的時候,執行循環的迭代,每次迭代時都會調用n(),意味着每次迭代都要對n()中的i進行賦值。
另外注意,前面說過,函數的默認參數是在函數聲明時進行賦值的,因此下面的列表L中每一個元素所表明的函數,它們的變量i都指向不一樣的數值對象。
def f1(): L = [] for i in range(5): def n(i=i): print(i) L.append(n) return L f1()[0]() f1()[1]() f1()[2]() f1()[3]() f1()[4]()
執行結果:
0 1 2 3 4