2017-05-29 55 views

回答

1

對於簡單的情況下,例如當代碼很短,我們沒有太多的變量來捕捉,我們可以創建一個臨時拉姆達並稱之爲:

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()()) 
相關問題