最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

根據(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,結果是?15
result2?=?closure2(5)??#?計算?20?+?5,結果是?25
print(result1)
print(result2)

在上面的示例中,outer_function 是外部函數(shù),它接受一個參數(shù) x,然后定義了一個內(nèi)部函數(shù) inner_function,它接受另一個參數(shù) y,并返回 x + y 的結果。當我們調(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)在我們打印兩個閉包看一下結果:

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

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

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

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)

輸出結果為:

16
17
26

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

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

在正式了解閉包相關的字節(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 保存的是字符串,也就是變量名,棧幀當中保存的是變量名字對應的真實對象,也就是函數(shù)參數(shù)),這么做的目的是為了方面后面字節(jié)碼 LOAD_CLOSURE 的操作,因為實際虛擬機存儲的是指向對象的指針,因此浪費不了多少空間。

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

下面我們分析一下和閉包相關的字節(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 對應的字節(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 的對象,對應的參數(shù)就是 x 。也就是將參數(shù) x 加載到棧幀上。

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

LOAD_CONST:加載對應的常量到棧幀當中,這里是會加載兩個常量,分別是函數(shù)對應的 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ù)組對應的下標 (oparg) 當中。

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

如果上面的過程太復雜,我們在這里從整體的角度再敘述一下,簡單說來就是當有代碼調(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ù)。

總結

在本篇文章當中詳細介紹了閉包的使用例子和使用原理,理解閉包最重要的一點就是函數(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)閉包的效果。

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

相關文章

最新評論

祁连县| 会宁县| 淮南市| 泗水县| 禄丰县| 房产| 广宗县| 江阴市| 黄骅市| 石嘴山市| 禹州市| 尼玛县| 会理县| 泰州市| 阿勒泰市| 双鸭山市| 加查县| 清水县| 赣州市| 嘉定区| 枞阳县| 石门县| 鄂托克前旗| 垫江县| 都兰县| 建湖县| 门头沟区| 米易县| 西藏| 双流县| 乳山市| 青岛市| 永川市| 威远县| 祁阳县| 巍山| 徐闻县| 马山县| 兴仁县| 南安市| 青神县|