2021年12月12日 星期日

Python:比較 anonymous function 與 closure

變數的作用域 (scope)

在查找一個變數時,首先會去找 local namespace (function),再找 global namespace (module),再找 built-in namespace。以下為一個例子:

num = 2
def set_num_1(val):
    num = val
def set_num_2(val):
    global num
    num = val
set_num_1(5) # num = 2
set_num_2(5) # num = 5

可以看出在第一個函數中只改到了 local namespace,而第二個函數加入了 global 才讓 num 變成在 global namespace 之中。 

Anonymous Function

一般的 python function 由 def 來定義,而一個被定義的函數若沒有名字則為 anonymous function。

Closure

Closure 也是個函數,但其綁定了一個環境,包含了一些 free variables 在之中。而這些 free variable 即使在定義的作用域已經無效了的時候仍然能夠被使用。以下為一個例子:

def make_averager():
    series = []
    def averager(val):
        series.append(val)
        return sum(series) / len(series)
    return averager

avg = make_averager()
print(avg(1)) # 1.0
print(avg(2)) # 1.5
print(avg(3)) # 2.0
print(avg.__code__.co_varnames) # ('val',)
print(avg.__code__.co_freevars) # ('series',)
print(avg.__closure__) # (list object at 0x7f898c80ee80,) 
print(avg.__closure__[0].cell_contents) # [1, 2, 3]
  • make_averager 為 enclosing function,而 averager 為 nested function。Nested function 用到了定義在 enclosing function 中的變數 series
  • Enclosing function 回傳 nested function。
  • 綁定的 free variable 即為 series
  • 可以用 __code__ 以及 __closure__ 來了解 closure 中的細節。

關鍵字 nonlocal

以下為簡化後的 averager 的例子:

def make_averager():
    count = 0
    total = 0

    def averager(val):
        nonlocal count, total
        count += 1
        total += val
        return total / count
    return averager

如果少了第六行中的 nonlocal發生此錯誤:local variable 'count' referenced before assignment。因此 nonlocal 的目的即為讓這兩個變數能為 free variables 而不只是 local variables。

什麼時候會用到 closure?

這篇文章有稍微介紹一個例子。

沒有留言:

張貼留言