闭包 closure
本篇教学会介绍 Python 里的“闭包 Closure”,进一步理解作用域 ( scope ) 和自由变数的概念。
快速导览:什么是闭包?、什么是作用域? 、闭包的基本范例、自由变数 nonlocal
本篇使用的 Python 版本为 3.7.12,所有范例可使用 Google Colab 实作,不用安装任何软件 ( 参考:使用 Google Colab )
什么是闭包 closure?
闭包,从字面的意思翻译就是一个“封闭的包裹”,在包裹外的人,无法拿到包裹里的东西,如果你在包裹里,就能尽情取用包裹内的东西,闭包可以保存在函数作用范围内的状态,不会受到其他函数的影响,且无法从其他函数取得闭包内的数据,也可避免建立许多全域变数互相干扰。
闭包的定义:
- A 函数中定义了 B 函数。
- B 函数使用了 A 函数的变数。
- A 函数回传了 B 函数。
下方的程序码,就是一个简单闭包的例子。
def a(msg):
i = '!!!' # ------------------------ 閉包開始
def b(): # A 函式內定義了 B 函式
print(msg + i) # B 函式使用了 A 函式的變數
return b # 將 B 函式當作回傳值 ------- 閉包結束
s = a('hello')
s() # 呼叫變數 s 所指向的閉包 B 函式,此時 B 函式記住了 'hello' (來自 A 函式的 msg) 和 '!!!' ( 來自 A 函式的 i ),因此輸出 'hello!!!'
什么是作用域 scope?
作用域 Scope 指的是变数、常数、函数或其他定义语句可以“被存取得到”的范围,Python 总共定义了四种作用域,从内而外分别是 Local ( 区域 )、Enclosing ( 闭包外函数 )、Global ( 全域 ) 和 Built-in ( 内置默认 ),内部的作用域无法影响到外部作用域。
闭包的范例应用
下方的程序码,会建立一个 avg 函数的闭包,执行后虽然 test() 执行了三次,但因为每次执行时保留下一个作用域的系结关系,所以会不断将传入的数值进行计算,最后就会得到 11 的结果。
def count(): # 建立一個 count 函式
a = [] # 函式內有區域變數 a 是串列
def avg(val): # 建立內置函式 avg ( 閉包 )
a.append(val) # 將參數數值加入變數 a
print(a) # 印出 a
return sum(a)/len(a) # 回傳 a 串列所有數值的平均
return avg # 回傳 avg
test = count()
test(10) # 將 10 存入 a
test(11) # 將 11 存入 a
test(12) # 印出 11
自由变数 nonlocal
不过如果将上方的例子,改成变数的做法,可能就会发生错误,因为在 cal 函数里的变数 a 后方使用了“等号”,意义等同于变数的“赋值”,换句话说是新建了一个区域变数 a,就造成了名称空间里名称重叠的问题。
def count(): # 建立一個 count 函式
a = 1 # 新增變數 a 等於 1
def cal(val): # 建立內置函式 cal ( 閉包 )
a = a + val # 設定變數 a 等於 a + val
return a # 回傳 a
return cal # 回傳 cal
test = count()
test(10)
test(11)
test(12)
如果必须这么做,可以使用 nonlocal 的方式,宣告这个变数是“自由变数”( 不是这个区域里的变数 ),就能正常使用这个变数。
def count():
a = 1
def cal(val):
nonlocal a # 宣告 a 為自由變數
a = a + val
return a
return cal
test = count()
test(10)
test(11)
test(12) # 34 ( 1 + 10 + 11 + 12 )
微信扫码关注
抖音扫码关注