從記憶體位址理解python的閉包

2021-09-11 07:14:23 字數 2796 閱讀 8201

紀錄一下目前對閉包的個人淺見:

閉包的概念:

存在內外層函式巢狀的情況

內層函式引用了外層函式的變數或者引數

外層函式把內層的這個函式本身當作返回值進行返回,而不是返回內層函式產生的某個值

舉個最簡單的例子:

def somebody(name):

def something(what):

print(name.title() + " wants to " + what + "!!!")

return something

result0 = somebody("lily")

result1 = somebody("jackie")

result0("eat cake") # lily wants to eat cake!!!

result1("do homework") # jackie wants to do homework!!!

上面是乙個最簡單的閉包,外層函式somebody()內定義了內層函式something(),並且內層函式呼叫到了外層的name做了乙個print的事情,然後外層函式返回的是內層函式something這個函式本身,而非something()。這裡我個人理解為是返回的外層函式輸入為某個值時,此時內層函式的位址,比如上面的result0接收的就是外層輸入為lily時,內層函式something的位址,因此result0仍然是乙個函式物件。

下面單步除錯驗證一下:

在外層輸入為lily時,可以看到外層函式返回的something函式位址是***x30fae8,即result0指向的也是***x30fae8位址(***x表示前面一長串懶得打的部分)

當外層輸入變成jackie時,可以看到此時返回的something函式位址變成***x33fae8了,即result1指向***x33fae8這個位址。

往下執行到result0("eat cake")時,可以發現記憶體位址跳到了之前***x30fae8的位址,也就是說之前的記憶體並沒有被釋放掉,而是作為當時外層函式狀態下返回的函式位址,因此直接跳到了print()那一行執行輸出。

再看乙個例子:

def test():

funcs =

for i in range(1,4):

def test2():

print(i)

return funcs

newfuncs = test()

newfuncs[0]() # 3

newfuncs[1]() # 3

newfuncs[2]() # 3

為什麼輸出的是333而不是123呢?實際上,newfuncs在獲得test()返回的函式位址後,包含的三個儲存為列表形式的函式位址的確是指向三個不同記憶體位址的,但是這三個記憶體位址指向的不同狀態下的三個test2()卻指向了同乙個i,即for迴圈的最後一次迴圈i=3。為什麼都指向同乙個i=3呢?這是因為,在外層test()中,每一次for迴圈都定義了乙個新的test2(),但此時內層函式test2()內i的值卻從未傳入過,還只是乙個變數標識而已。最後一次迴圈i的值是3,因此在newfuncs[0]()這一步時,進入相應的記憶體位址指向的print(i),這是內層函式開始引用i的值了,而此時i早已是3了,所以內層函式直接引用到了i=3這個結果。

def test():

funcs =

for i in range(1,4):

def test2():

print(i)

print(id(i)) # 內層函式引用的i的位址

print(id(i)) # for迴圈時i的位址

return funcs

newfuncs = test()

newfuncs[0]()

newfuncs[1]()

newfuncs[2]()

執行一下:

140730656088912

140730656088944

140730656088976

3140730656088976

3140730656088976

3140730656088976

可以發現for迴圈時i的位址是變化的,而呼叫到內層函式引用i的值值,往外層尋找i只能尋找到最後一次for迴圈也就是i=3時的位址了。但是注意,儘管如此,外層函式每次return的函式位址也是不一樣的。

那如何改才能使輸出的i是123而不是333呢?

def test():

funcs =

for i in range(1,4):

def test2(num):

def inner():

print(num)

return inner

return funcs

newfuncs = test()

newfuncs[0]() # 1

newfuncs[1]() # 2

newfuncs[2]() # 3

總結一下,閉包返回的是函式位址(所以返回的時候沒有括號!),而非函式物件。對於某一外層函式輸入狀態下的閉包返回函式,其位址指向的內層函式引用的所有外層、內層的變數和表示式都是當時狀態下固定好了的。

理解記憶體位址

物理記憶體 記憶體條實際提供的記憶體空間 記憶體定址 在記憶體上找到正確的位置以便進行訪問的過程 硬編碼 通過實體地址操作物理記憶體的寫碼方式 線性記憶體和物理記憶體 相似點 從0編號,線性增加 不同 1.實體地址一一對應於實際物理記憶體空間的位置,而線性位址可多對一 多個線性位址對應乙個實體地址 ...

python檢視記憶體位址

python 通過id檢視記憶體位址 eg a 1 print id a 輸出94486697843200不可變物件是指物件的記憶體值不能被改變。python中變數以引用的方式指向物件,如果變數引用了不可變物件,當改變該變數時,由於其所指的物件的值不能被改變,相當於把原來的值複製乙份後再改變,這會開...

記憶體位址和記憶體空間的理解

1.記憶體位址用4位16進製制和8位16進製表示的區別。例如經常可以看到某些書籍上寫的記憶體位址0x0001,在另外一些書籍上寫的記憶體位址又變成了0x00000001。都是表示的編號為1的記憶體位址,為什麼乙個是4位16進製表示,另外乙個又是用8位16進製表示呢?首先,必須要知道記憶體位址只是乙個...