這種便捷的函數定義方式哗总,如果翻譯成我們熟知的函數形式几颜,會是這個樣子:
也就是說,Python 中的 lambda 函數是可以接收多個參數的函數讯屈,返回值是一個表達式蛋哭。
它最大的好處是單行簡潔,不需要函數命名與換行縮進涮母。
不得不說谆趾,匿名函數有時候是挺好用的,比如下文會介紹到的一些常見用法叛本,它因此受到了不少人的推崇沪蓬。
但是,匿名函數通常也會造成代碼難以閱讀来候,容易被人濫用跷叉,再加上 Python 只提供了對它的“殘疾的”支持,所以又有一些觀點不建議使用匿名函數吠勘。
現實上性芬,Python 之父Guido van Rossum就屬于“不推薦使用派”,只不過最后妥協了剧防,他甚至曾經(2005年)想要移除lambda植锉。
lambda 這一個由其他開發(fā)者貢獻進來的特性(借鑒自 lisp 語言),存在了十多年峭拘,但是卻被這門語言的創(chuàng)造者(兼首席設計師)所嫌棄俊庇,最后竟然還奇跡般地幸存了下來狮暑,對于這個故事,大家是否覺得挺有戲劇性的辉饱?
接下來搬男,本文就仔細聊一聊這個處境尷尬卻生命力頑強的 lambda 匿名函數吧!
1彭沼、lambda如何使用缔逛?
lambda 函數通常的用法是結合 map()、reduce()姓惑、filter()褐奴、sorted() 等函數一起使用,這些函數的共性是:都可以接收其它函數作為參數于毙。
例如下面的幾個例子:
初學者也許會覺得代碼讀不懂敦冬,但是只要記住“Python中的函數是一等公民”,知道一個函數可以被作為另一個函數的參數或者返回值唯沮,就容易理解了脖旱。
比如對于 map() 函數的例子,你可以理解成這個形式:
甚至可以還原成普通的函數:
map() 函數的第一個參數是一個函數介蛉,第二個參數是一個可迭代對象萌庆。這第一個參數會迭代地調用第二個參數中的元素,調用的結果以迭代器的形式返回甘耿。
這個例子使用了 list()踊兜,是為了方便一次性取出迭代器中的元素,直觀地展示出來佳恬,在實際使用中,很可能會是基于迭代器的形式于游。
以上幾種用法毁葱,我們可以總結出 lambda 函數的使用規(guī)律:
它出現在需要使用函數的地方
它一般不會被獨立使用,總是作為其它函數的一部分
它適合實現簡單的功能
它是一次性的用途贰剥,不能在其它地方復用
2倾剿、lambda 有什么問題?
由上面的用法可以看出蚌成,使用 lambda 函數的代碼比較緊湊簡潔前痘,所以有人稱它體現了“Pythonic”的優(yōu)雅思想。
但是担忧,lambda 函數就沒有什么缺陷嗎芹缔?
有!當前的 lambda 函數有一個最大的問題瓶盛,即只支持單行表達式最欠,無法實現豐富的功能示罗,例如無法在函數創(chuàng)建時使用語句(statement),無法使用 if-else 的判斷條件芝硬,也無法使用 try-except 的異常捕獲機制蚜点,等等。
這極大地限制了它的能力拌阴,導致了它被人詬病為“殘疾的”绍绘。
從技術實現的角度上看,這個問題可以通過語法層面的設計來解決迟赃。
在當年的郵件組討論中陪拘,有人提出過一些解決思路,比如這封郵件:
它提出了一個lambda args::suite的想法捺氢,支持寫成這樣的形式:
但是藻丢,Guido 很快就否決了這個思路。
寫了一篇文章《Language Design Is Not Just Solving Puzzles》來回應:
其基本觀點是:不能光顧著解決一個問題/實現某種功能摄乒,就引入缺乏“Pythonicity”的語言設計悠反。
那么,為什么 Guido 會認為這一種是不好的設計呢馍佑?
我嘗試著概括一下斋否,理由是:
雙冒號“::”憑空在此引入,但是跟切片語法中的“::”完全不同拭荤,而且跟 C++/Perl 中的作用域操作符用法也不同
在 lambda 中實現其它功能并不重要茵臭,這還會讓解析器變得復雜(需區(qū)分是否有縮進、記錄縮進級別)舅世,顯得小題大做了
即使不用雙冒號旦委,用其它符號表示(比如單冒號),還是難以接受雏亚,因為都會在一個表達式中嵌入縮進代碼塊缨硝。這就跟使用花括號和 begin/end 關鍵字來作語句分組(statement grouping)一樣,都令人難以接受
簡而言之罢低,他認為簡潔友好的用戶體驗更為重要查辩,如果簡潔的語法無法滿足需求,就應該寫成具名函數的形式网持,而非設計出復雜的匿名函數宜岛。
3、為什么 Guido 特想移除 lambda功舀?
上文提到的多行 lambda 語句(multi-statement lambda)事件發(fā)生在 2006 年萍倡,我們看到了 Guido 不想給 lambda 引入復雜設計的原因。
但是日杈,早在 2005 年遣铝,Guido 就曾經想要從 Python 移除 lambda佑刷,他對它的“嫌棄”是一個“歷史悠久”的傳統……
在《The fate of reduce() in Python 3000》這篇短文中,Guido 提出要一次性移除 reduce()酿炸、map()瘫絮、filter() 以及 lambda。
移除 lambda理由如下:
很多人誤以為匿名函數能做嵌套函數不能做的事填硕,但其實并無區(qū)別麦萤;存在lambda,就會造成不必要的選擇扁眯,減少選擇壮莹,可以簡化思維
移除 reduce()、map() 和 filter() 后姻檀,就沒必要寫簡短的局部函數了
回顧一下我們在前文中總結出的 lambda 的 4 條使用規(guī)律命满,可以發(fā)現它跟幾個高階函數(可以接收其它函數作為參數的函數)有較強的“寄生關系”,如果它們能移除了的話绣版,lambda 確實就沒有什么獨立存留的意義了胶台。
那么,為什么 Guido 覺得應該移除那幾個高階函數呢杂抽?
主要的理由有:
可以替換成更加清晰的列表解析式或者生成器表達式诈唬,例如 filter(P,S) 可以寫成 [x for x in S if P(x)],map(F, S) 寫成 [F(x) for x in S]
至于 reduce()缩麸,他說這是最討厭的铸磅,除了涉及 + 和 * 的少數用法,其它時候他總要拿出紙筆來畫圖解才能搞清楚杭朱。除了顯式地寫循環(huán)阅仔,他還針對 reduce() 的幾種用法而提出了幾個替代用法,包括引入新的 any() 和 all() 函數
總體而言弧械,Guido 的想法暗合了《The Zen of Python》中的這一條:There should be one-- and preferably only one --obvious way to do it霎槐。
但是回到現實,為了照顧某些人的習慣梦谜,以及對兼容性的考慮,Guido 最后保守地放棄了“清理異端”的計劃袭景。
因此唁桩,lambda 得以從 Python 最高獨裁者的手上死里逃生。直到一年后耸棒,它試圖興風作浪(多行表達式)荒澡,卻慘遭鎮(zhèn)壓。
我仿佛聽到了 Guido 的內心 OS:當初我想刪除東西的時候与殃,你們百般阻撓单山,現在你們卻想添加東西碍现,哼,沒門米奸!……
哈哈昼接,是開了個玩笑。
Guido 的所有決定都體現了他的 Pythonic 設計美學悴晰、自恰的邏輯一致性以及對社區(qū)聲音的權衡慢睡。
對于 lambda,我認可他的觀點铡溪,我覺得 Python 的理解變得更為豐富了漂辐。不知道你們是何感想?