給妹子講python-S01E19解析Python內嵌做用域與函數閉包

歡迎關注公衆號:python數據科學家python

【要點搶先看】閉包

1.python中獨特的嵌套函數
2.嵌套做用域與閉包現象
3.nonlocal關鍵字與內嵌做用域變量修改app

前情回顧,上一節咱們介紹了變量的LEGB索引機制:對一個變量,首先在本地(函數內)查找;以後查找嵌套函數的本地做用域,而後再是查找當前的全局做用域。函數

到目前爲止,咱們還有一個做用域沒有介紹,就是嵌套做用域,即E,他是嵌套函數的本地做用域。spa

【妹子說】什麼是嵌套函數?code

python有一個頗有意思的地方,就是def函數能夠嵌套在另外一個def函數之中。調用外層函數時,運行到的內層def語句僅僅是完成對內層函數的定義,而不會去調用內層函數,除非在嵌套函數以後又顯式的對其進行調用。orm

x = 99

def f1():
    x = 88
    def f2():
        print(x)
    f2()

f1()

88
複製代碼

能夠看出,f1中的嵌套變量x覆蓋了全局變量x=99,而後f2中的本地變量按照引用規則,就引用了x=88。cdn

下面咱們來講說嵌套做用域的一個特殊之處:對象

本地做用域在函數結束後就當即失效,而嵌套做用域在嵌套的函數返回後卻仍然有效。索引

def f1():
    x = 88
    def f2():
        print(x)
    return f2

action = f1()
action()

88
複製代碼

這個例子很是重要,也頗有意思,函數f1中定義了函數f2,f2引用了f1嵌套做用域內的變量x,而且f1將函數f2做爲返回對象進行返回。最值得注意的是咱們經過變量action獲取了返回的f2,雖然此時f1函數已經退出結束了,可是f2仍然記住了f1嵌套做用域內的變量名x。

上面這種語言現象稱之爲閉包:一個能記住嵌套做用域變量值的函數,儘管做用域已經不存在。

這裏有一個應用就是工廠函數,工廠函數定義了一個外部的函數,這個函數簡單的生成並返回一個內嵌的函數,僅僅是返回卻不調用,所以經過調用這個工廠函數,能夠獲得內嵌函數的一個引用,內嵌函數就是經過調用工廠函數時,運行內部的def語句而建立的。

def maker(n):
    k = 8
    def action(x):
        return x ** n + k
    return action

f = maker(2)
print(f)

<function maker.<locals>.action at 0x00000000021C51E0>
複製代碼

再看一個例子:

def maker(n):
    k = 8
    def action(x):
        return x ** n + k
    return action

f = maker(2)
print(f(4))

24
複製代碼

這裏咱們能夠看出,內嵌的函數action記住了嵌套做用域內得兩個嵌套變量,一個是變量k,一個是參數n,即便後面maker返回並退出。咱們經過調用外部的函數maker,獲得內嵌的函數action的引用。這種函數嵌套的方法在後面要介紹的裝飾器中會常常用到。這種嵌套做用域引用,就是python的函數可以保留狀態信息的主要方法了。

這裏接着說說另外一個關鍵字nonlocal

本地函數經過global聲明對全局變量進行引用修改,那麼對應的,內嵌函數內部想對嵌套做用域中的變量進行修改,就要使用nonlocal進行聲明。

def test(num):
    in_num = num
    def nested(label):
        nonlocal in_num
        in_num += 1
        print(label, in_num)
    return nested

F = test(0)
F('a')
F('b')
F('c')

1
2
3
複製代碼

這裏咱們能夠看到幾個點,咱們在nested函數中經過nonlocal關鍵字引用了內嵌做用域中的變量in_num,那麼咱們就能夠在nested函數中修改他,即便test函數已經退出調用,這個「記憶」依然有效。

再看最後一個例子:

def test(num):
    in_num = num
    def nested(label):
        nonlocal in_num
        in_num += 1
        print(label, in_num)
    return nested

F = test(0)
F('a')
F('b')
F('c')

G = test(100)
G('mm')

1
2
3
mm 101
複製代碼

屢次調用工廠函數返回的不一樣內嵌函數副本F和G,彼此間的內嵌變量in_num是彼此獨立隔離的。

【妹子說】這個內嵌做用域以及閉包的特性確實很是特別,確實很pythonic啊!

公衆號二維碼:python數據科學家:

相關文章
相關標籤/搜索