另一個Lambda表示式教程

mtunique發表於2015-01-24

有很多Python的lambda教程[1]。最近我偶然發現一個,真挺有用的。是Mike Driscoll在Mouse VS Python 部落格上的關於lambda的討論) 。

Mike的討論非常好:清晰,直接,且含有實用的示例。它幫助我終於領會了lambda,並導致我寫的另一篇lambda教程。

一個用來建構函式的工具 基本上,Python的lambda是用於建構函式(或更精確地說,函式物件)的工具。這意味著,Python有兩個建構函式的工具:def和lambda。

下面是一個例子。您可以以正常的方式用def構造一個函式,就像這樣:

或者你可以用lambda

下面是lambda的其他的一些有趣的例子:

lambda的好處在哪裡? 已經困擾我有很長一段時間的一個問題是:lambda的好處在哪裡?為什麼我們需要lambda?

答案是: 我們並不需要lambda,我們不用它一樣可以做所有的事情。但是… 在特定的情況下,很是方便 – 它讓編寫程式碼更容易一些,而且編寫的程式碼更整潔。

什麼樣的情況? 好,其中一個情況是,我們需要一個簡單的一次性功能:將被只使用一次函式。

通常,寫函式有兩個目的:(a)以減少程式碼重複(b)模組化程式碼。

  • 如果你的應用程式在不同的地方包含重複的程式碼塊,那麼你就可以把程式碼拷貝到一個函式,給函式名,然後 – 使用該函式名 – 在程式碼中的不同位置呼叫它。
  • 如果你有一個程式碼塊執行一個明確的操作 – 但真的是冗長、粗糙、破壞程式的可讀性,那麼你可以把那麼長的粗糙的所有程式碼變成一個函式。

但是,假設你需要建立一個函式,將只被使用一次 – 只從應用程式中的一個地方呼叫。好吧,首先,你不需要給函式的名稱。它可以是“匿名的”。而且你可以把它定義在你想使用它的地方。這就是lambda是非常有用的時候。

但是,但是,但是…你會說。

  • 首先是,為什麼你想要一個只呼叫一次函式?排除原因(a)。
  • 一個lambda的函式體只能包含單個表示式。這意味著,lambda表示式必須很短。排除了原因(b)。

創造一個短的匿名函式可能的原因是什麼?

那麼,考慮一下程式碼片段,使用lambda來定義一個Tkinter的GUI介面按鈕的行為。 (這個例子是來自Mike的教程。)

這裡要記住的一點是,tk.Button需要一個函式物件作為引數傳遞給該函式的引數。該函式物件將是它(按鈕)點選按鈕時呼叫的函式。基本上,該函式指定了點選該按鈕時,GUI會做什麼。

因此,我們必須通過函式引數傳遞一個函式物件到一個按鈕。並注意 – 因為不同的按鈕做不同的事情 – 我們需要為每個按鈕物件提供不同的函式物件。每個函式將只使用一次。 所以,儘管我們可以這樣寫

這樣寫更容易(且更清楚)

當一個GUI程式有這樣的程式碼,該按鈕物件需要“call back”到被提供給作為其命令的函式物件。 因此,我們可以說,lambda的最常見的用途之一是在GUI框架,如Tkinter和wxPython中寫“回撥(callback)”,。

這一切似乎很簡單。所以… 為什麼lambda如此難以理解? 我能想到四個原因:

首先Lambda難以理解,因為:一個lambda只能用一個表示式:什麼是表示式?

很多人想知道這個問題的答案。如果你在Google上搜尋了一下,你會看到很多的帖子,“在Python中,表示式和語句之間的區別是什麼?”

一個很好的答案是,表示式返回(或計算結果為)值,而語句則沒有。不幸的是,在Python中表示式也可以是一個語句,這種情況很容易造成糊塗。 – 賦值語句就像 A = B = 0,Python支援鏈式賦值。 (Python不是C)

很多情況下在當人們問這個問題時,他們真正想知道的是:什麼樣的情況下可以放入lambda,什麼情況下不可以? 而對於這個問題,我覺得遵循一些簡單的規則就足夠了。

  • 如果它不返回一個值,它不是一個表示式,不能放入一個lambda。
  • 如果你能想象它在賦值語句中放在等號的右邊,那它是一個表示式,可以放進一個lambda。

利用這些規則意味著:

  1. 賦值語句不能在lambda中使用。在Python中,賦值語句不返回任何東西,甚至沒有None(null)。
  2. 如數學運算,字串操作,列表解析等都是一個lambda。
  3. 函式呼叫是表示式。可以在lambda中放置函式呼叫,並將引數傳遞給該函式。這樣就在一個新的匿名函式中封裝了原函式呼叫(引數其他內容)。
  4. 在Python3,print成了一個函式,所以在Python3+,print(…)可以在lambda中使用。
  5. 即使函式是返回None,就像在Python3print函式,可以在一個lambda中使用。
  6. [條件表示式],它是在Python2.5中引入,是表示式(而不是僅僅是一個語法不同的if / else語句)。它們返回一個值,並且可以在一個lambda使用。

難以理解的第二個原因是:一個lambda只有一個表示式:為什麼?為什麼只有一個?為什麼不能多表示式?為什麼不能是語句?

對於一些開發人員來說,這個問題的意思是為什麼Python的lambda語法如此怪異?對於其他人,尤其是那些有Lisp的背景的,這個問題是指為什麼Python的lambda這麼殘廢?為什麼不像Lisp的lambda那麼強大?

答案是很複雜,它涉及Python語法的“pythonicity”。lambda是一個相對較晚加入Python的。它加入的時候,Python語法已經固定下來了。在這種情況下,語法的lambda必須用“Pythonic”的方式硬塞進一個已經建立好的Python語法中。導致可以在lambda表示式上來完成一些事情有一定的侷限性。

坦率地說,我仍然認為lambda語法看起來有點怪異。儘管那樣,但是Guido解釋了為什麼lambda的語法是不會改變的。 Python不會成為Lisp。

難以理解的第三個原因是::lambda通常被描述為一種工具,用於建立函式,但lambda語句中不含有返回語句。

在某種意義上,return語句隱含在lambda中。lambda規範必須包含只有一個表示式,表示式必須返回一個值,由lambda建立一個匿名函式隱式地返回表示式的返回值。這非常有意義。

還是 – 我想缺乏一個明確的return語句使得很難理解lambda,或者至少很難迅速理解。

難以理解的第四個原因是在lambda教程中通常會用作為建立匿名函式來引入lambda,其實最常見的lambda用途是用於建立匿名過程。

在程式設計的上古時期,我們就將子程式區分為兩種不同的形式:過程和函式。過程是用來做事情的,並沒有返回任何東西。函式是用於計算和返回值。函式和過程之間的差異已經成為一些程式語言的一部分了。在Pascal,例如,程式和函式是不同的關鍵字。

在大多數現代語言中,語言的語法中不再區分過程和函式。 例如Python的函式,可以像過程,函式,或兩者兼而有之。(不是完全理想的)結果是一個Python函式總是被稱為“函式”,即使它是本質上充當過程。

雖然過程和函式之間的區別已經基本消失的語言結構中,當思考有關程式如何工作的時候我們仍然時常用它。例如,當我讀一個程式的原始碼,並看到一些函式F,我揣摩F是做什麼的。我經常可以把它歸類到一個過程或函式 – 我會對自己說“F的目的是做這個的”,或“F的目的是計算和返回等這個和這個的”。

所以現在我想我們可以明白為什麼lambda的許多解釋是難以理解。 First of all, the Python language itself masks the distinction between a function and a procedure. 首先,Python語言本身模糊了函式和過程的區別。

第二,大多數教程介紹把lambda作為建立匿名函式的工具來介紹,其主要目的是要計算並返回結果。在大多數教程看到(這個包含)的第一個例子展示瞭如何編寫一個lambda來返回值,x的平方根。

但是,這不是lambda最常用的方式,不是當他們在Google上搜尋“python lambda教程”的時候要找的。對於lambda最常見的用途是建立匿名的過程,在GUI回撥中使用。在這些用例中,我們不關心什麼lambda返回什麼,我們關心它做了什麼。

這就解釋了為什麼典型的Python程式設計師難以理解大多數的lambda說明。因為他嘗試學習如何編寫一些GUI框架的程式碼:Tkinter,wxPython。執行這些lambda,想理解他們。Google“python lambda教程”。他發現那些以例子開始的教程是完全不適合他。

所以,如果你是這樣的程式設計師 – 本教程是給你寫的。我希望它能幫助到你。對不起,我們在本教程的結尾看到了這點,而不是開頭。我們希望有一天,有人會寫一個lambda教程,而不是以這種方式開頭

  • lambda是一個用來構造匿名函式的工具

而以這樣的句子開始:

  • lambda是一個用來構造回撥的工具

所以你需要有它。另一個lambda教程。