python 學習彙總53:閉包(中級學習- tcy)
阿新 • • 發佈:2018-11-23
閉包的定義,及基本概念;例項;閉包定義中的典型錯誤分析及解決辦法;閉包在實踐的應用。
閉包 建立時間:2018/8/11 修改時間:2018/11/18
1.定義:
閉包:是由函式及其相關的引用環境組合而成實體(即:閉包=函式+引用環境) 內部函式引用外部作用域(非全域性)變數,會把引用環境和函式體打包成一個整體(閉包)返回,內部函式被認為是閉包(closure). 函式: 可作另一個函式引數或返回值,賦給變數。函式可巢狀定義; 每次呼叫ExFunc函式返回新閉包例項,例項之間是隔離; 一個函式可返回一個計算結果或函式。返回一個函式時,牢記該函式並未執行,返回函式中不要引用任何可能會變化的變數 引用環境 指在程式執行中的某個點所有處於活躍狀態的約束(變數名字和其所代表的物件之間的聯絡)所組成的集合。
2.例項:
def ExFunc(n): # 函式返回的就是閉包 sum = n def InsFunc(): # 內嵌函式是閉包 return sum + 1 # 引用到外層函式中的區域性變數sum return InsFunc # 返回值是函式 myFunc = ExFunc(10) # 返回新閉包例項 a1 = myFunc() # 11 myAnotherFunc = ExFunc(20) # 返回新閉包例項 a2 = myAnotherFunc() # 21 print(a1, a2) 例項2 def add(x): def adder(y): return x + y return adder a = add(8) b=add type(a) #<type 'function'> a.__name__ #'adder' a(10) #18 b(8)(10) #18
2.使用閉包注意事項:
2.1.閉包中不能修改外部作用域區域性變數 例項1:-每次呼叫閉包函式時都對變數a進行遞增的操作 #使用閉包時經典錯誤程式碼 def foo(): a = 1 def bar(): # nonlocal a a = a + 1 #修改外部作用域區域性變數 #python規則指定所有在賦值語句左面的變數都是區域性變數 return a return bar c = foo() print(c()) #錯誤 #解決方法 def foo(): a = [1] def bar(): a[0] = a[0] + 1 return a[0] return bar
2.2. 迴圈結束迴圈體中臨時變數i不會銷燬存在於執行環境中: 注意:
1)返回閉包時:返回函式不要引用任何迴圈變數,或後續會發生變化變數。 2)如要引用迴圈變數 方法1:該函式建立一個引數,用這個引數繫結到迴圈變數 ;如例項 1 方法2:迭代 方法;參見例項 2 再建立一個函式,用該函式的引數繫結迴圈變數當前的值, 無論該迴圈變數後續如何更改,已繫結到函式引數的值不變
例項2:
#python的函式只有在執行時,才會去找函式體裡的變數的值。
lst = []
for i in range(3):
def foo(x):
print (x + i)
lst.append(foo)
for f in lst:
f(2) #4,4,4
#解決方法
for i in range(3):
def foo(x,y=i):
print( x + y)
lst.append(foo)
例項3 :
def count():
lst = []
for i in range(1, 4):
def f():
return i*i
lst.append(f)
return lst
f1, f2, f3 = count()
f1(),f2(),f3() #9,9,9
#解決辦法
def count():
def f(j):
def g():
return j*j
return g
lst = []
for i in range(1, 4):
lst.append(f(i)) # f(i)立刻被執行,因此i的當前值被傳入f()
return lst
# 結果:
f1, f2, f3 = count()
f1(),f2(),f3() #1,4,9
3.作用
閉包主要是在函式式開發過程中使用。
用途1
當閉包執行完後,仍然能夠保持住當前的執行環境。
希望函式每次執行結果基於這個函式上次的執行結果。
例棋盤遊戲:
棋盤大小為50*50,左上角為座標系原點(0,0),我需要一個函式,接收2個引數,分別為方向(direction),
步長(step),該函式控制棋子的運動。棋子運動的新的座標除了依賴於方向和步長以外,當然還要根據原
來所處的座標點,用閉包就可以保持住這個棋子原來所處的座標。
origin = [0, 0] # 座標系統原點
legal_x = [0, 50] # x軸方向的合法座標
legal_y = [0, 50] # y軸方向的合法座標
def create(pos=origin):
def player(direction,step):
# 先判斷引數direction,step合法性,direction不能斜走step不能為負
# 對新生成的x,y座標合法性判斷處理,
new_x = pos[0] + direction[0]*step
new_y = pos[1] + direction[1]*step
pos[0] = new_x
pos[1] = new_y #注意!此處不能寫成 pos = [new_x, new_y]
return pos
return player
player = create() # 建立棋子player,起點為原點
print player([1,0],10) # 向x軸正方向移動10步
print player([0,1],20) # 向y軸正方向移動20步
print player([-1,0],10) # 向x軸負方向移動10步
輸出:
[10, 0]
[10, 20]
[0, 20]
用途2:
閉包可以根據外部作用域的區域性變數來得到不同的結果,這有點像一種類似配置功能的作用,
我們可以修改外部的變數,閉包根據這個變數展現出不同的功能。
比如有時我們需要對某些檔案的特殊行進行分析,先要提取出這些特殊行。
def make_filter(keep):
def the_filter(file_name):
file = open(file_name)
lines = file.readlines()
file.close()
filter_doc = [i for i in lines if keep in i]
return filter_doc
return the_filter
# 取得檔案"result.txt"中含有"pass"關鍵字的行
filter = make_filter("pass")
filter_result = filter("result.txt")