13. 閉包

(1) global, nonlocal及local變數

以下是一個例子:

以下的程式透過呼叫outer_func()來產生inner_func()物件,每個inner_func()的呼叫次數存在cnt2中,所有inner_func()的呼叫次數存在cnt1中。

cnt1 = 0

def outer_func(name):
    cnt2 = 0
    
    def inner_func():        
        global cnt1
        nonlocal cnt2
     
        cnt1+=1       
        cnt2+=1        
        cnt3 = cnt1 - cnt2
        
        print(f'函式:{name}, 所有函式呼叫次數:{cnt1}, 自己呼叫次數:{cnt2}, 其他函式呼叫次數:{cnt3}')
        
    return inner_func

對於內層的inner_func()函式而言, 第1行的cnt1是global變數; 第4行的cnt2是nonlocal變數; 第12行的cnt3是local變數

主程式:

a = outer_func('Tom')
b = outer_func('Mary')

a()
b()
a()
a()

執行結果:

函式:Tom, 所有函式呼叫次數:1, 自己呼叫次數:1, 其他函式呼叫次數:0 函式:Mary, 所有函式呼叫次數:2, 自己呼叫次數:1, 其他函式呼叫次數:1 函式:Tom, 所有函式呼叫次數:3, 自己呼叫次數:2, 其他函式呼叫次數:1 函式:Tom, 所有函式呼叫次數:4, 自己呼叫次數:3, 其他函式呼叫次數:1

說明

(2) 閉包, closure

建立閉包有下面3個步驟:

  1. 建立一個函式(外部函式)內建立一個內部函式(inner function);

  2. 讓內部函式使用外部函式的變數(對內部函式而言是nonlocal variable);

  3. 回傳內部函式。

因此,閉包包括了內部函式,也包括它使用的外在函式之變數及其內容。

以下是一個例子:

def gen_counter(cnt):
    def counter():
        nonlocal cnt        
        cnt+=1      
        return cnt
        
    return counter 

第2-5行,是一個內部函式counter(),它在第3行時引用了外部函式的變數cnt。 第7行,回傳內部函式。

主程式:

a = gen_counter(0)  #從0開始累加

print(a())
print(a())
print(a())
print(a())

執行結果:

1 2 3 4

說明:

問題:

from urllib.request import urlopen

def index(url):
    def get():
        return urlopen(url).read().decode('utf8')
    return get

#--------------------------------------
getNTUB = index("http://www.ntub.edu.tw") 
print(getNTUB()) 

請修改以上程式,主程式部分改成以下,讀入的網頁將寫到"ntub.html"檔案中。

getNTUB = index("http://www.ntub.edu.tw", "ntub.html") 
print(getNTUB()) 

Last updated