1. 程式人生 > >閉包Closure

閉包Closure

恩~ 如期來啦“閉包”~

一、函式作為返回值

介紹“閉包”之前,先了解一下函式作為返回值的情況。高階函式除了可以接收函式作為引數外,還可以把函式作為結果值返回。例如之前介紹的裝飾器中,就出現了將函式作為返回值。

二、閉包

1、產生閉包的條件以及作用

什麼是閉包?當在函式中巢狀另一個函式時,如果內部函式引用了外部函式的變數,則可能產生閉包。所以閉包產生的三個條件(缺一不可)

  • 1、必須巢狀一個內部函式
  • 2、內部函式必須引用外部函式的變數
  • 3、外部函式必須返回內部函式

那為什麼要試用閉包,閉包的作用呢?

  • 1、閉包可以根據外部函式的區域性變數來得到不同的結果
  • 2、當閉包執行完成後,仍可以保持當前的執行環境,執行結果依賴於該函式上一次的執行結果

2、閉包舉例

栗子一:求序列之和

>>> def calc_sum(*args):
...     ax = 0
...     for n in args:
...         ax = ax + n
...     return ax  # 返回變數
...
>>> calc_sum(1,2,3)
6

但是,現在如果要求不需要立即取得求和結果,而是在後面的程式碼中,根據需要再計算,該怎麼弄呢?我們可以不返回求和的結果,而返回求和的函式,如下:

>>>def lazy_sum(*args):
...    def sum():            # sum()是內部函式,可以利用外部函式的引數
...        ax = 0
...        for n in args:    # sum()中使用外部函式的區域性變數
...            ax = ax + n 
...        return ax
...    return sum            # 形成閉包,此時,*args儲存在返回的函式中
...
>>>f = lazy_sum(1,3,5,7,9)
>>>f          # 此時返回的是求和函式
>>> f()       # 呼叫函式f()時,才真正計算求和的結果
25

注意:

  • lazy_sum()函式的內部執行順序,執行f時,執行到return sum處,*args儲存在返回函式中,返回的是sum()函式。當執行f()時,相當於執行sum(),且包含*args
  • 當我們呼叫lazy_sun()時,每次都會返回一個新的函式,即使傳入相同的引數,但是f()呼叫結果不影響。

我們來驗證第二點:

# 但是呼叫 f1() 與f2()的呼叫結果互不影響
>>> f1 = lazy_sum(1,3,5,7,9)
>>> f2 = lazy_sum(1,3,5,7,9)
>>> f1
<function lazy_sum.<locals>.sum at 0x013DD618>
>>> f2
<function lazy_sum.<locals>.sum at 0x02F92DF8>
>>> f1 == f2
False
>>> f1() == f2()
True
>>> f1()
25
>>> f2()
25
>>> id(f1())
1627215984
>>> id(f2())
1627215984

說明:f1f2返回函式的位置不一樣,所以f1==f2返回結果為False。但是不影響最後的執行結果,f1()f2()的執行結果均為25,且用id()進行檢視,指向是同一塊區域。

栗子二:

def count():
    fs = []
    for i in range(1, 4):
        def f():         # 返回函式f()放在迴圈裡
            return i*i
    fs.append(f)
    return fs
f1, f2, f3 = count()

實際執行結果為:f1=9 f2=9 f3=9可能與實際想的([1,4,9])有點不一樣。因為f()函式放在了for迴圈裡,只有當迴圈結束後,最後才返回i=3的執行結果9。所以返回函式最好不要引用任何迴圈變數,或者說後續可能變化的量。那如何來修改呢?

def count():
    def f(j):
        def g():
            return j*j      # 形成閉包
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i))      # 一個i值進入後,f(i)立刻被執行,並加入到fs中
    return fs

f1, f2, f3 = count()  # 返回函式g沒有引用j

最後結果:[1,4,9] 即f1=1 f2=4 f3=9

三、匿名函式lambda

  • 定義:匿名函式指一類無需定義識別符號函式名的函式或者子程式。Python允許使用lambda關鍵字創造匿名函式。
  • 語法lambda 引數:表示式 或者 lambda 形參1,…,形參n : function(形參),入參1,…,入參n
  • 注意:1、lambda函式可以接收任意多個引數並且返回單個表示式的值; 2、lambda中不能包含命令,返回的表示式不能超過一個。
  • 優點:1、可以省去定義函式的過程,精簡程式碼; 2、對於一些抽象的、不會重複使用的函式可以用lambda進行定義。

例子:

>>> list( map( lambda x: x*x ,[1,2,3] ) )
[1, 4, 9]

其中lamdba x : x*x 實現的是:def f(x): return x*x 的功能。

  • 可以把匿名函式賦值給一個變數,再利用變數呼叫該函式。例如:
>>> f = lambda x:x*x  
>>> f(5) # 呼叫
>>> g = lambda x,y=2 : x*y
>>> g(2,4)
8
>>> g(2)    # 預設y=2
4
  • 可以把匿名函式作為返回值返回,例如:
return lambda x:x*x

❤ thanks for watching, keep on updating...