測開之函數進階· 第6篇《閉包》

堅持原創輸出,點擊藍字關注我吧

做者:清菡
博客:oschina、雲+社區、知乎等各大平臺都有。python

因爲微信公衆號推送改成了信息流的形式,防止走丟,請給加個星標 ⭐,你就能夠第一時間接收到本公衆號的推送!

目錄

  • 1、非閉包
  • 2、閉包安全

    • 1.閉包的概念
    • 2.閉包的做用
  • 3、函數的__closure__屬性

1、非閉包

見過了在函數中調用函數自己,在函數內部定義一個函數:微信

def func():
    print('-----func被調用--------')
    def count_book():
        print('這個是計算買書方式的函數')

# func()是在外面定義的,能夠直接調用func()
func()

在外面能夠調用裏面的函數嗎?閉包

不能夠。相對於外部而言,def count_book()這個函數名是局部的,是函數內部的一個局部變量,因此在外部是訪問不了函數內部的數據。函數

在函數內部能夠訪問外面的,可是在函數外面是訪問不了裏面的。測試

在外面定義個函數:spa

def login():
    print('登陸')

def func():
    login()
    print('-----func被調用--------')
    def count_book():
        print('這個是計算買書方式的函數')

# func()是在外面定義的,能夠直接調用func()
func()

在函數裏面是能夠調用的,由於def login()它是個全局變量。code

要想在外面調用裏面的def count_book()函數,有什麼辦法呢?對象

加個return,把這個函數給返回回來。接下來func()函數調用以後,它會有個返回值,返回值就是count_book()。用res接收下,接收到了以後,經過res()再調用。遞歸

調用方式一:

def login():
    print('登陸')


def func():
    login()
    print('-----func被調用--------')

    def count_book():
        print('這個是計算買書方式的函數')
    return count_book


# func()是在外面定義的,能夠直接調用func()
res = func()
res()

調用方式二:

def login():
    print('登陸')


def func():
    login()
    print('-----func被調用--------')

    def count_book():
        print('這個是計算買書方式的函數')
    return count_book


# func()是在外面定義的,能夠直接調用func()

# 方式二
func()()

# 方式一
# res = func()
# res()

以上代碼不是閉包,只是符合閉包的前 2 個條件,不符合條件「內層函數對外部做用域有一個非全局的變量引用」。

2、閉包

1.閉包的概念

一個完整的閉包須知足如下 3 個條件:

  • 函數中嵌套了一個函數
  • 外層函數返回內層函數的變量名
  • 內層函數對外部做用域有一個非全局的變量引用

num = 100是外層函數裏定義的一個變量,不是全局變量。

以上,這種形式的函數被稱爲閉包。

全局變量:變量是定義在模塊裏,哪一個地方均可以用。

例如:

非全局變量:

不帶參數的閉包:

def func():
    num = 100
    def count_book():
        print(num)
        print('這個是計算買書方式的函數')
    return count_book

帶參數的閉包:

def func(num):
    def count_book():
        print(num)
        print('這個是計算買書方式的函數')
    return count_book

# func()是在外面定義的,能夠直接調用func()

# 方式二
# func()()

# 方式一
res = func(2020)
res()

雖然num不是在外置函數中定義的,可是經過函數參數傳進來的,傳到func()的命名空間裏面,print(num)在內部是能夠引用到func()命名空間裏面的值的。

這裏的num不是全局變量,它是func()命名空間裏面的一個變量,一個數據,是經過參數func(2020)傳進來的。

這個也是閉包,也知足閉包的三個條件。

2.閉包的做用

實現數據的鎖定,提升穩定性。

遞歸函數在函數調用的時候是這樣的:

遞歸調用原理圖

在一個函數裏面調用自身的時候,它又有塊區間放這個函數,它內部有塊又調用了,它會繼續在內存裏面把這個函數給存起來,繼續這樣遞歸下去,很是佔內存。

閉包,它沒有遞歸。

函數調用的運行機制:

定義函數的時候,運行文件,Python 解釋器從上往下執行代碼,檢測到def login()的時候,會在內存裏面找一塊地址,讓函數名指向這個地址。

當你在下面再次調用這個函數的時候,Python 解釋器直接運行這個內存地址裏面的代碼,也就是函數內部的代碼。

代碼從上往下運行的時候,檢測到有個func(),來個地址把func()裏面的代碼,拿到地址裏。下面調用的時候就至關於直接運行地址裏面的代碼了。

從上往下運行,又檢測到一個函數count_book(),這個時候又會把這個函數名拿出來,而後再往下走,它直接返回了函數。

函數名拿出來以後,把這個函數名拿出來放到了這裏,這個時候會給它再畫出來一塊地址。而後讓這個count_book()函數指向這個地址,有在調用count_book()它的時候,會運行裏面的代碼。

這裏沒有調用,把count_book()這個函數名返回出來了。

count_book()這個函數名是在func()的命名空間裏面。調用的時候返回到res這個地方來了。返回到全局變量裏來了,經過res來接收下,res其實又指向這塊內存地址了。在外面經過res調用的時候,就會運行這個內存地址裏面的代碼。

代碼中有傳入參數num,函數裏面引用外層的變量num,這個變量和它放在同一個空間裏面。

3、函數的__closure__屬性

每一個函數裏面都有一個這樣的屬性:

res.__closure__

這個屬性存儲的是:當前的這個函數它裏面的代碼以及這個函數對外部非全局變量引用的一個數據。

當是閉包的時候,返回這樣一個結果:

返回一個對象。這裏存儲的就是 2020。

將代碼修改一下:

def func(num,b):
    def count_book():
        print(num)
        print(b)
        print('這個是計算買書方式的函數')
    return count_book

res = func(2020,'qinghan')
print(res.__closure__)

閉包函數引用的非全局變量,會存儲在這個函數自身的一個__closure__屬性裏面,當要用的時候,直接從屬性裏面拿就好了。

經過這種方式實現數據鎖定,提升數據的安全性。

閉包函數須要使用到外部變量,爲了不使用的外部的變量發生變化。內部所用到的外部的變量,給鎖定到閉包函數自身的__closure__屬性裏面。

這時候外部的環境發生任何變化,對它都是沒有影響的。同時也不會對外層的環境形成影響。

全局變量的時候返回 None:

若是一個閉包裏面引用了全局變量,那麼就不算閉包了,例如:

引用全局變量了就沒辦法實現數據鎖定了。


公衆號清菡軟件測試首發,更多原創文章:清菡軟件測試 115+原創文章,歡迎關注、交流,禁止第三方擅自轉載。

相關文章
相關標籤/搜索