Python 閉包 (Closure)
這里介紹一下python 的閉包
基本概念
閉包(closure)是函數式編程的重要的語法結構。
函數式編程的一個特點就是,允許把函數本身作為參數傳入另一個函數,還允許返回一個函數!
Python對函數式編程提供部分支持。由於Python允許使用變量,因此,Python不是純函數式編程語言。
閉包(closure)是函數式編程的重要的語法結構。函數式編程是一種編程范式 (而面向過程編程和面向對象編程也都是編程范式)。
在面向過程編程中,我們見到過函數(function);在面向對象編程中,我們見過對象(object)。
函數和對象的根本目的是以某種邏輯方式組織代碼,並提高代碼的可重復使用性(reusability)。
閉包也是一種組織代碼的結構,它同樣提高了代碼的可重復使用性。
不同的語言實現閉包的方式不同。Python以函數對象為基礎,
為閉包這一語法結構提供支持的 (我們在特殊方法與多范式中,已經多次看到Python使用對象來實現一些特殊的語法)。
Python一切皆對象,函數這一語法結構也是一個對象。
在函數對象中,我們像使用一個普通對象一樣使用函數對象,比如更改函數對象的名字,或者將函數對象作為參數進行傳遞。
函數對象的作用域
和其他對象一樣,函數對象也有其存活的范圍,也就是函數對象的作用域。
函數對象是使用def語句定義的,函數對象的作用域與def所在的層級相同。
比如下面代碼,我們在next函數的隸屬范圍內定義的函數test,就只能在test的隸屬范圍內調用。
def test(): def next(): print('next') print('test') test() # 執行結果 > test
再看下面代碼
def test(): def next(): print('next') next() print('test') next() test() # 執行結果 > next > test > next
引入閉包
函數是一個對象,所以可以作為某個函數的返回結果。
def hello(greet): def setName(name): print(greet,name) return setName Hello = hello("Good Morning") Hello('Yang') print(dir(Hello)) print(Hello.__closure__) print(Hello.__closure__[0].cell_contents) print(Hello.__name__) print(id(Hello)) Hellob = hello("Good Afternoon") Hellob('Yang') print(Hello.__name__) print(id(Hellob)) # 執行結果 #> Good Morning Yang #> ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__'] #> (<cell at 0x0000026D9A8078B8: str object at 0x0000026D9A966370>,) #> Good Morning #> setName #> 1779595040560 #> Good Afternoon Yang #> setName #> 1779595040696
閉包只是在表現形式上跟函數類似,但實際上不是函數。
從代碼的結果中可以看到,閉包在運行時可以有多個實例,不同的引用環境變量(這里就是greet變量)和相同的函數(這里就是setName)組合可以產生不同的實例。
一個函數和它的環境變量合在一起,就構成了一個閉包(closure)。在Python中,所謂的閉包是一個包含有環境變量取值的函數對象。環境變量取值被保存在函數對象的__closure__屬性中。
__closure__里包含了一個元組(tuple)。這個元組中的每個元素是cell類型的對象。我們看到第一個cell包含的就是Good Morning,也就是我們創建閉包時的環境變量greet的取值。
Python中怎么創建閉包
在Python中創建一個閉包可以歸結為以下三點:
- 閉包函數必須有內嵌函數
- 內嵌函數需要引用該嵌套函數上一級中的變量
- 閉包函數必須返回內嵌函數
通過這三點,就可以創建一個閉包
閉包實例
def closureFun(): '''閉包''' a = 5 def add(x): # 1 閉包函數必須有內嵌函數 return x + a # 內嵌函數需要引用該嵌套函數上一級中的變量 a return add # 閉包函數必須返回內嵌函數 print(locals()) c = closureFun() # 實例化函數 closureFun 返回 函數add sum = c(6) # 調用add ,並傳參 ,此時返回 x + a = 6 + 5 = 11 print(sum) # 11 print(globals())
Python中的內建函數locals()和globals()可以用來查看不同namespace中定義的元素。
總結
本文介紹了如何通過Python創建一個閉包,以及Python創建的閉包是如何工作的。