1. 程式人生 > >Python3學習筆記(十二):閉包

Python3學習筆記(十二):閉包

ram 類型 trac 就是 不能 ast 兩種 作用 ror

閉包定義:

在一個外函數中定義了一個內函數,內函數裏引用了外函數的臨時變量,並且外函數的返回值是內函數的引用。這樣就構成了一個閉包。

我們先來看一個簡單的函數:

def outer(a):
    b = 10
    print(a+b)

這個函數定義了一個局部變量b,我們來調用這個函數看一下

>>> outer(5)
15
>>> print(b)
Traceback (most recent call last):
  File "<pyshell#42>", line 1, in <module>
    print
(b) NameError: name b is not defined

可以看到在調用完函數outer後,再打印變量b時,會報變量b沒定義。這個我們很容易理解:b是函數outer的一個局部變量,調用函數結束後,函數的內部所有東西都會釋放掉,還給內存,局部變量都會消失。

我們再來看一個嵌套函數:

def outer(a):
    b = 10
    def inner():
        print(a+b)
    return inner

這個函數的返回值是一個函數對象,也就是inner函數,我們來調用下這個函數

>>> demo = outer(5)      #
返回的是inner函數 >>> demo() # 這裏就相當於執行inner() 15

看到這裏是不是感覺到有點奇怪,執行demo = outer(5)後,就是調用outer函數結束了,按道理說這個時候outer函數內的所有東西都會釋放掉,包括局部變量a,b。那麽為什麽我在執行demo()時,還可以打印出a+b的值呢?

這就是一種特殊情況,按照文章開始處閉包的定義,這個嵌套函數其實就是一個閉包,它有一種特權:如果外函數在結束的時候發現有自己的臨時變量將來會在內部函數中用到,就把這個臨時變量綁定給了內部函數,然後自己再結束。

我們知道什麽是閉包了,也知道在閉包中外函數會把變量綁定到內函數上,那麽在內函數中能不能修改外函數的變量呢?

我們來試一下:

def outer(a):
    b = 10
    def inner():
        b += 1
        print(b)
    return inner

調用:

>>> demo = outer(5)
>>> demo()
Traceback (most recent call last):
  File "<pyshell#47>", line 1, in <module>
    demo()
  File "C:\Users\lmj\AppData\Local\Programs\Python\Python36\1.py", line 4, in inner
    b += 1
UnboundLocalError: local variable b referenced before assignment

報錯,看來是不能直接修改,得想其他辦法。

在函數作用域中,我們知道如果要在函數中修改全局變量,有兩種方法:

  • global 聲明全局變量
  • 全局變量是可變數據類型

同樣,在閉包中也是類似情況,在內函數中想修改外函數綁定到內函數的變量時,也有兩種方法:

  • 用nonlocal 關鍵字聲明 一個變量, 表示這個變量不是局部變量空間的變量,需要向上一層變量空間找這個變量。
  • 把外函數變量改成可變類型數據進行修改
def outer(a):
    b = 10
    c = [a]     # 把外函數變量修改成列表
    def inner():
        nonlocal b   # nonlocal聲明外函數變量
        b += 1
        c[0] += 1
        print(b)
        print(c[0])
    return inner

調用:

>>> demo = outer(5)
>>> demo()
11
6

Python3學習筆記(十二):閉包