欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入理解python虛擬機如何實現(xiàn)閉包

 更新時間:2023年10月07日 14:47:57   作者:一無是處的研究僧  
當能夠從設(shè)計者的層面去理解閉包就再也不用死記硬背一些閉包的概念了,所以本文就來從虛擬機層面和大家一起討論函數(shù)閉包是如何實現(xiàn)的

在本篇文章當中主要從虛擬機層面討論函數(shù)閉包是如何實現(xiàn)的,當能夠從設(shè)計者的層面去理解閉包就再也不用死記硬背一些閉包的概念了,因為如果你理解閉包的設(shè)計原理之后,這些都是非常自然的。

根據(jù) wiki 的描述,a closure is a record storing a function together with an environment。所謂閉包就是將函數(shù)和環(huán)境存儲在一起的記錄。這里有三個重點一個是函數(shù),一個是環(huán)境(簡單說來就是程序當中變量),最后一個需要將兩者組合在一起所形成的東西,才叫做閉包。

Python 中的閉包

我們現(xiàn)在用一種更加直觀的方式描述一下閉包:閉包是指在函數(shù)內(nèi)部定義的函數(shù),它可以訪問外部函數(shù)的局部變量,并且可以在外部函數(shù)執(zhí)行完后繼續(xù)使用這些變量。這是因為閉包在創(chuàng)建時會捕獲其所在作用域的變量,然后保持對這些變量的引用。下面是一個詳細的Python閉包示例:

def?outer_function(x):
????#?外部函數(shù)定義了一個局部變量?x
????def?inner_function(y):
????????#?內(nèi)部函數(shù)可以訪問外部函數(shù)的局部變量?x
????????return?x?+?y
????#?外部函數(shù)返回內(nèi)部函數(shù)的引用,形成閉包
????return?inner_function
#?創(chuàng)建兩個閉包實例,分別使用不同的?x?值
closure1?=?outer_function(10)
closure2?=?outer_function(20)
#?調(diào)用閉包,它們?nèi)匀豢梢栽L問其所在外部函數(shù)的?x?變量
result1?=?closure1(5)??#?計算?10?+?5,結(jié)果是?15
result2?=?closure2(5)??#?計算?20?+?5,結(jié)果是?25
print(result1)
print(result2)

在上面的示例中,outer_function 是外部函數(shù),它接受一個參數(shù) x,然后定義了一個內(nèi)部函數(shù) inner_function,它接受另一個參數(shù) y,并返回 x + y 的結(jié)果。當我們調(diào)用 outer_function 時,它返回了一個對 inner_function 的引用,形成了一個閉包。這個閉包可以保持對 x 的引用,即使 outer_function 已經(jīng)執(zhí)行完畢。

在上面的例子當中 outer_function 的返回值就是閉包,這個閉包包含函數(shù)和環(huán)境,函數(shù)是 inner_function ,環(huán)境就是 x,從程序語義的層面來說返回值是一個閉包,但是如果直接從 Python 層面來看,返回值也是一個函數(shù),現(xiàn)在我們打印兩個閉包看一下結(jié)果:

>>> print(closure1)
<function outer_function.<locals>.inner_function at 0x102e17a60>
>>> print(closure2)
<function outer_function.<locals>.inner_function at 0x1168bc430>

從上面的輸出結(jié)果可以看到兩個閉包(從 Python 層面來說也是函數(shù))所在的內(nèi)存地址是不一樣的,因此每次調(diào)用都會返回一個不同的函數(shù)(閉包),因此兩個閉包相互不影響。

再來看下面的程序,他們的執(zhí)行結(jié)果是什么?

def?outer_function(x):
?def?inner_function(y):
??nonlocal?x
??x?+=?1
??return?x?+?y
?return?inner_function
closure1?=?outer_function(10)
closure2?=?outer_function(20)
result1?=?closure1(5)
print(result1)
result1?=?closure1(5)
print(result1)
result2?=?closure2(5)
print(result2)

輸出結(jié)果為:

16
17
26

根據(jù)上面的分析 closure1 和 closure2 分別是兩個不同的閉包,兩個閉包的 x 也是各自的 x ,因此前一個閉包的 x 變化并不會影響第二個閉包,所以 result2 的輸出結(jié)果為 26。

閉包相關(guān)的字節(jié)碼

在正式了解閉包相關(guān)的字節(jié)碼之前我們首先來重新回顧一下 CodeObject 當中的字段:

def?outer_function(x):
?def?inner_function(y):
??nonlocal?x
??x?+=?1
??return?x?+?y
?print(inner_function.__code__.co_freevars)??#?('x',)
?print(inner_function.__code__.co_cellvars)??#?()
?return?inner_function
if?__name__?==?'__main__':
?out?=?outer_function(1)
?print(outer_function.__code__.co_freevars)??#?()
?print(outer_function.__code__.co_cellvars)??#?('x',?)

cellvars 表示在其他函數(shù)當中會使用本地定義的變量,freevars 表示本地會使用其他函數(shù)定義的變量。在上面的例子當中,outer_function 當中的變量 x 會被 inner_function 使用,而cellvars 表示在其他函數(shù)當中會使用本地定義的變量,所以 outer_function 的這個字段為 ('x', )。

上面的內(nèi)容我們簡要回顧了一下 CodeObject 當中的兩個非常重要的字段,這兩個字段在進行傳遞參數(shù)的時候非常重要,當我們在進行函數(shù)調(diào)用的時候,虛擬機會新建一個棧幀,在進行新建棧幀的過程當中,如果發(fā)現(xiàn) co_cellvars 存儲的字符串變量也是函數(shù)參數(shù)的時候,除了會在局部變量當中保存一份參數(shù)之外,還會將傳遞過來的參數(shù)保存到棧幀對象的其他位置當中(這里需要注意一下,CodeObject 當中的 co_freevars 保存的是字符串,也就是變量名,棧幀當中保存的是變量名字對應(yīng)的真實對象,也就是函數(shù)參數(shù)),這么做的目的是為了方面后面字節(jié)碼 LOAD_CLOSURE 的操作,因為實際虛擬機存儲的是指向?qū)ο蟮闹羔?,因此浪費不了多少空間。

實際在虛擬機的棧幀對象當中 freevars 是一個數(shù)組,后續(xù)的字節(jié)碼都是會根據(jù)數(shù)組下標對這些變量進行操作。

下面我們分析一下和閉包相關(guān)的字節(jié)碼操作

def?outer_function(x):
?def?inner_function(y):
??nonlocal?x
??x?+=?1
??return?x?+?y
?return?inner_function
if?__name__?==?'__main__':
?import?dis
?dis.dis(outer_function)

上面的代碼回輸出 outer_function 和 inner_function 對應(yīng)的字節(jié)碼:

  2           0 LOAD_CLOSURE             0 (x)
              2 BUILD_TUPLE              1
              4 LOAD_CONST               1 (<code object inner_function at 0x100757a80, file "closure_bytecode.py", line 2>)
              6 LOAD_CONST               2 ('outer_function.<locals>.inner_function')
              8 MAKE_FUNCTION            8 (closure)
             10 STORE_FAST               1 (inner_function)
  7          12 LOAD_FAST                1 (inner_function)
             14 RETURN_VALUE
Disassembly of <code object inner_function at 0x100757a80, file "closure_bytecode.py", line 2>:
  4           0 LOAD_DEREF               0 (x)
              2 LOAD_CONST               1 (1)
              4 INPLACE_ADD
              6 STORE_DEREF              0 (x)
  5           8 LOAD_DEREF               0 (x)
             10 LOAD_FAST                0 (y)
             12 BINARY_ADD
             14 RETURN_VALUE

我們現(xiàn)在來詳細解釋一下上面的字節(jié)碼含義:

LOAD_CLOSURE:這個就是從棧幀對象當中加載指定下標的 cellvars 變量,在上面的字節(jié)碼當中就是加載棧幀對象 cellvars 當中下標為 0 的對象,對應(yīng)的參數(shù)就是 x 。也就是將參數(shù) x 加載到棧幀上。

BUILD_TUPLE:從棧幀當中彈出 oparg (字節(jié)碼參數(shù)) 個參數(shù),并且將這些參數(shù)封裝成元祖,在上面的程序當中 oparg = 1 。

LOAD_CONST:加載對應(yīng)的常量到棧幀當中,這里是會加載兩個常量,分別是函數(shù)對應(yīng)的 CodeObject 和函數(shù)名。

在執(zhí)行完上的字節(jié)碼之后棧幀當中 valuestack 如下所示:

MAKE_FUNCTION:這條字節(jié)碼的主要作用是根據(jù)上面三個棧里面的對象創(chuàng)建一個函數(shù),其中最重要的字段就是 CodeObject 這里面保存了函數(shù)最重要的代碼,最下面的元祖就是 inner_function 的 freevars,當虛擬機在創(chuàng)建函數(shù)的時候就已經(jīng)把這個對象保存下來了,然后在創(chuàng)建棧幀的時候會將這個對象保存到棧幀。需要注意的是這里所保存的變量就是函數(shù)參數(shù) x,他們是同一個對象。這就使得內(nèi)部函數(shù)每次調(diào)用的時候都可以使用參數(shù) x 。

我們再來看一下函數(shù) inner_function 的字節(jié)碼

  • LOAD_DEREF:這個字節(jié)碼會從棧幀的 freevars 數(shù)組當中加載下標為 oparg 的對象,freevars 就是剛剛在創(chuàng)建函數(shù)的時候所保存的,也就是 outter_function 傳遞給 inner_function 的元祖。直觀的來說就是將外部函數(shù)的 x 加載到 valuestack 當中。
  • STORE_DEREF:就是將棧頂?shù)脑貜棾?,保存?cellvars 數(shù)組對應(yīng)的下標 (oparg) 當中。

后續(xù)的字節(jié)碼就很簡單了,這里不做詳細分析了。

如果上面的過程太復(fù)雜,我們在這里從整體的角度再敘述一下,簡單說來就是當有代碼調(diào)用 outer_function 的時候,傳遞進來的參數(shù),會在 outer_function 創(chuàng)建函數(shù) inner_function 的時候當作閉包參數(shù)傳遞給 inner_function,這樣 inner_function 就能夠使用 outer_function 的參數(shù)了,因此這也不難理解,每次我們調(diào)用函數(shù) outer_function 都會返回一個新的閉包(實際就是返回的新創(chuàng)建的函數(shù)),因為我們每次調(diào)用函數(shù) outer_function 時,它都會創(chuàng)建一個新的函數(shù),而這些被創(chuàng)建的函數(shù)唯一的區(qū)別就是他們的閉包參數(shù)不同。這也就解釋了再之前的例子當中為什么兩個閉包他們互不影響,因為函數(shù) outer_function 創(chuàng)建了兩個不同的函數(shù)。

總結(jié)

在本篇文章當中詳細介紹了閉包的使用例子和使用原理,理解閉包最重要的一點就是函數(shù)和環(huán)境,也就是和函數(shù)綁定在一起的變量。當進行函數(shù)調(diào)用的時候函數(shù)就會創(chuàng)建一個新的內(nèi)部函數(shù),也就是閉包。在虛擬機內(nèi)部實現(xiàn)閉包主要是通過函數(shù)參數(shù)傳遞和函數(shù)生成實現(xiàn)的,當執(zhí)行 MAKE_FUNCTION 創(chuàng)建新函數(shù)的時候,會將外部函數(shù)的閉包變量 (在文章中就是 x ) 傳遞給內(nèi)部函數(shù),然后保存在內(nèi)部函數(shù)當中,之后的每一次調(diào)用都是用這個變量,從而實現(xiàn)閉包的效果。

到此這篇關(guān)于深入理解python虛擬機如何實現(xiàn)閉包的文章就介紹到這了,更多相關(guān)python虛擬機內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • python實現(xiàn)字符串中字符分類及個數(shù)統(tǒng)計

    python實現(xiàn)字符串中字符分類及個數(shù)統(tǒng)計

    這篇文章主要介紹了python實現(xiàn)字符串中字符分類及個數(shù)統(tǒng)計,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • Python web如何在IIS發(fā)布應(yīng)用過程解析

    Python web如何在IIS發(fā)布應(yīng)用過程解析

    這篇文章主要介紹了Python web如何在IIS發(fā)布應(yīng)用過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-05-05
  • Opencv+Python識別PCB板圖片的步驟

    Opencv+Python識別PCB板圖片的步驟

    這篇文章主要介紹了Opencv+Python識別PCB板圖片的步驟,幫助大家更好的理解和使用python進行機器學習,感興趣的朋友可以了解下
    2021-01-01
  • python字符串運算符詳情

    python字符串運算符詳情

    這篇文章主要介紹了 python字符串運算符詳情,在編程里,用的最多的就是字符串,字符串同時也是各類數(shù)據(jù)的中轉(zhuǎn)站,下文基于python的相關(guān)資料介紹字符串運算符,需要的小伙伴可以參考一下
    2022-04-04
  • 如何使用Python一鍵修改上萬個文件名

    如何使用Python一鍵修改上萬個文件名

    各位有沒有遇到過需要批量整理多層文件夾結(jié)構(gòu)里各種類型(docx、excel、ppt)的文件材料的時候,下面這篇文章主要給大家介紹了關(guān)于如何使用Python一鍵修改上萬個文件名的相關(guān)資料,需要的朋友可以參考下
    2022-05-05
  • Python中的日期時間處理詳解

    Python中的日期時間處理詳解

    Python程序能用很多方式處理日期和時間。轉(zhuǎn)換日期格式是一個常見的例行瑣事,這篇文章主要介紹了Python中的日期時間處理的幾種方式的區(qū)別和聯(lián)系,需要的朋友可以參考下
    2016-11-11
  • 詳解Python中Addict模塊的使用方法

    詳解Python中Addict模塊的使用方法

    Addit是一個Python模塊,除了提供標準的字典語法外,Addit 生成的字典的值既可以使用屬性來獲取,也可以使用屬性進行設(shè)置。本文將詳細講講它的使用方法,需要的可以參考一下
    2022-05-05
  • 對python同一個文件夾里面不同.py文件的交叉引用方法詳解

    對python同一個文件夾里面不同.py文件的交叉引用方法詳解

    今天小編就為大家分享一篇對python同一個文件夾里面不同.py文件的交叉引用方法詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-12-12
  • Python matplotlib實現(xiàn)散點圖的繪制

    Python matplotlib實現(xiàn)散點圖的繪制

    Matplotlib作為Python的2D繪圖庫,它以各種硬拷貝格式和跨平臺的交互式環(huán)境生成出版質(zhì)量級別的圖形。本文將利用Matplotlib庫繪制散點圖,感興趣的可以了解一下
    2022-03-03
  • Python爬蟲教程之利用正則表達式匹配網(wǎng)頁內(nèi)容

    Python爬蟲教程之利用正則表達式匹配網(wǎng)頁內(nèi)容

    這篇文章主要給大家介紹了關(guān)于Python爬蟲教程之利用正則表達式匹配網(wǎng)頁內(nèi)容的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-12-12

最新評論