2010-04-28 69 views
6

我一直在爲自動化構建構建基本的測試框架。下面的代碼代表了使用不同程序的兩臺機器之間通信的簡單測試。在我實際做任何測試之前,我想完全定義它們 - 所以下面的測試實際上並沒有運行,直到所有的測試都被聲明爲止。這段代碼只是一個測試的聲明。Python Lambdas和變量綁定

remoteTests = [] 
for client in clients: 
    t = Test(
     name = 'Test ' + str(host) + ' => ' + str(client), 
     cmds = [ 
      host.start(CMD1), 
      client.start(CMD2), 

      host.wait(5), 

      host.stop(CMD1), 
      client.stop(CMD2), 
     ], 
     passIf = lambda : client.returncode(CMD2) == 0 
    ) 
remoteTests.append(t) 

無論如何,測試運行後,它會運行'passIf'定義的函數。因爲我想爲多個客戶端運行此測試,所以我正在迭代它們併爲每個客戶端定義一個測試 - 沒什麼大不了的。但是,在第一個客戶端上運行測試後,'passIf'將在客戶端列表中的最後一個評估,而不是在lambda聲明時的'客戶端'。

我的問題是:什麼時候python綁定lambdas中的變量引用?我想如果使用lambda之外的變量是不合法的,那麼解釋者就不知道我在說什麼。相反,它默默地綁定到最後一個「客戶端」的實例。

另外,有沒有辦法像我打算的那樣強制解決方案?

回答

7

client變量在外部範圍定義,因此由時間lambda運行它將總是被設置爲在列表中最後一個客戶端。

要獲得預期的結果,你可以給拉姆達的參數有默認值:

passIf = lambda client=client: client.returncode(CMD2) == 0 

由於默認值在拉姆達定義的時間進行評估,其價值將保持正確的。

的另一種方法是創建一個功能內的拉姆達:

def createLambda(client): 
    return lambda: client.returncode(CMD2) == 0 
#... 
passIf = createLambda(client) 

這裏拉姆達指client變量在createLambda功能,它具有正確的值。

+0

使用默認值可以很好地工作。謝謝! – stringer 2010-04-28 16:27:55

5

會發生什麼情況是您的passIf參數lambda指的是來自封閉範圍的變量client。它不涉及變量client在創建時引用的對象,而是變量本身。如果在循環結束後調用這些passIf,則意味着它們都引用循環中的最後一個值。 (在封閉術語,Python的閉包是後期綁定,不早期綁定

幸運的是這很容易使後期綁定閉合成一個早期綁定關閉。您可以通過簡單地給拉姆達與爲默認值的參數做到這一點,你要綁定:

passIf = lambda client=client: client.returncode(CMD2) == 0 

這是否意味着功能得到額外的參數,威力亂七八糟的東西,如果它被用參數來調用無意中 - 或者當你想要函數採取任意參數。所以另一種技術就是這樣做:

# Before your loop: 
def make_passIf(client): 
    return lambda: client.returncode(CMD2) == 0 

# In the loop 
t = Test(
    ... 
    passIf = make_passIf(client) 
) 
+0

好的解釋。我覺得這使得它更清晰:)。 「它不是指變量客戶端在創建時引用的對象,而是變量本身。」 **並且由於變量客戶端是可變的,所以在循環結束時,它指向最後一個「客戶端」對象。因此,這個價值依然存在。** – narayan 2016-01-28 07:50:11