0
如果我如何在Python中不變地捕獲閉包變量的CURRENT值?
def f():
a = 1
g = lambda: a
a = 2
return g
print(f()())
被印刷是2
,因爲g
被構造後a
被突變的值。
如何獲得g
以靜態捕獲a
的值,以便稍後修改被忽略?
如果我如何在Python中不變地捕獲閉包變量的CURRENT值?
def f():
a = 1
g = lambda: a
a = 2
return g
print(f()())
被印刷是2
,因爲g
被構造後a
被突變的值。
如何獲得g
以靜態捕獲a
的值,以便稍後修改被忽略?
對於簡單的情況下,例如當代碼很短,我們沒有太多的變量來捕捉,我們可以創建一個臨時拉姆達並稱之爲:
def f():
a = 1
g = (lambda a: lambda: a)(a)
a = 2
return g
這裏的問題是,代碼會很快變得難以閱讀。
或者,我們可以捕捉到變量作爲可選參數:
def f():
a = 1
g = lambda a=a: a
a = 2
return g
問題這裏,當然,我們可能不希望主叫方能夠指定這個參數。
(和代碼可以少一點可讀了。)
一個完全通用的解決方案可能是下面的,但它不捕獲全局:
def bind(*args, **kwargs):
# Use '*args' so that callers aren't limited in what names they can specify
func = args[0]
include_by_default = args[1] if len(args) > 1 else None
# if include_by_default == False, variables are NOT bound by default
if include_by_default == None: include_by_default = not kwargs
fc = func.__code__
fv = fc.co_freevars
q = func.__closure__
if q:
ql = []
i = 0
for qi in q:
fvi = fv[i]
ql.append((lambda v: (lambda: v).__closure__[0])(
kwargs.get(fvi, qi.cell_contents))
if include_by_default or fvi in kwargs
else qi)
i += 1
q = q.__class__(ql)
del ql
return func.__class__(fc, func.__globals__, func.__name__, func.__defaults__, q)
我不試圖在這裏捕捉全局變量的原因是語義可能會令人困惑 - 如果一個內部函數表示global x; x = 1
,那肯定是確實是想要全局變量x
需要修改,所以抑制這種變化很快會使代碼很不直觀。
然而,除非這一點,我們就能夠簡單地使用它,如下所示:
def f():
a = 1
g = bind(lambda: a)
a = 2
return g
print(f()())
,瞧,a
立即被抓獲。如果我們只想捕獲一些變量,我們可以這樣做:
def f():
a = 1
b = 2
g = bind(lambda: a + b, b=5) # capture 'b' as 5; leave 'a' free
a = 2
b = 3
return g
print(f()())