關於lambda function的二三事

閱讀時間約 13 分鐘
最初在Python官網的tutorial文件中看到lambda function時,雖然文件中有幾個使用範例,也在網路上看了一些相關的教學文章,但實在是看不出來有什麼非要用這玩意兒的理由。或許就如官網文件中所說的,lambda function就只是syntactic sugar而已,所以也就沒特別在意,直到在設計Game of Life的輸入介面時,因為需要用到,兜兜轉轉,費了好些功夫和時間,總算對它的用途和用法有比較完整的認識。
在Game of Life的輸入介面中,最重要也最不容易處理的部分,應該是怎麼樣可以讓人很容易輸入原始的universe樣態。既然universe是棋盤狀的方格,而每個方格,也就是cell的狀態,不是生,就是死,那最好的方式,就是可以用滑鼠點選方格來改變狀態。要想做到這樣的功能,在Python官網文件提到的module中,turtle和tkinter,是看起來比較像是含有所需功能的兩個module。
turtle主要是用來繪圖,同時也提供了處理滑鼠事件及鍵盤事件所需的工具。工具看來很齊全,不過要拿來處理universe的輸入問題,可得花費一番功夫。首先,要先畫出棋盤狀的方格。然後當使用者點選某個方格時,要透過滑鼠事件偵測滑鼠游標的位置,並計算這個位置是在哪個方格,接著改變這方格裡面所有像素的顏色。當然啦,最後還必須紀錄這個方格的狀態。唉呦!實在是太繁雜了,想想都覺得煩,不想寫。
古聖先賢總是教訓我們:做人要甘願、要勤勞,不可以偷懶!可是說實在的,要是太認命,面對工作就一頭栽進去埋頭苦幹,從不多動動腦子,想想是不是有什麼辦法可以簡化工作、提昇效率,現在哪來那麼進步的科技?不說別的,先前寫「核心計算」部分,在判斷cell是不是位於universe邊界的時候,要不是看著一堆if不順眼,同時也覺得,要考慮那麼多不同的邊界狀況,實在是有夠麻煩的,又怎會去想辦法設計出更乾淨俐落又有效的方法來?所以啊,要多想想是不是有那種可以偷懶,但卻有效的辦法。
既然不想用turtle做,那用tkinter行不行?tkinter主要是用來設計GUI,所以像是按鈕、標籤、視窗、捲軸、選單等各種常見的元件,一應俱全。不過挺討厭的是,有些元件在不同的程式語言中,有不同的名稱,所以明明知道元件的長相,還得費一番功夫找出對應的名稱。唉!就不能大家都叫一樣的名稱嗎?鐵鎚就長那樣,你叫它榔頭,它還是長那樣;checkbox就長那樣,你叫它checkbutton,它還是長那樣,那幹嘛弄個不同的名稱作弄人?很煩咧!不過煩歸煩,還是要想想有沒有哪個元件合用。
在tkinter中,每個元件的長相、功能都是固定的,能夠調整的東西就那些,所以現在主要的問題是:universe是棋盤狀的方格,用滑鼠點選方格時,方格的狀態以及顏色會改變。有哪些元件可以做出這樣的效果?先前在考慮用turtle做時,是把universe看成是一大塊,然後在裡頭畫出小方格。很顯然的,對於tkinter的元件來說,這樣子的做法行不通,得想想是不是有其他辦法。
既然在一大塊裡頭畫出小方格的方式行不通,那用一堆小方格組成一大塊行不行?在寫「核心計算」部分時,就曾經想過類似的問題。那時候在想:是要把universe看成是點陣圖,圖裡頭的pixel就代表cell,還是要把所有代表cell的方格看成是一個一個獨立的物件,由這些獨立的物件拼湊組成universe?這兩種方式都可以達到目的,差別只在於程式好不好寫,還有處理的效率。後來之所以採用點陣圖的方式,是覺得這樣會比較好寫。那現在如果把方格看成是物件,透過滑鼠的點選可以改變顏色,還能同時處理資料,有沒有哪個tkinter的元件具備這樣的功能?嘿嘿嘿!那個網路上到處都有的「按鈕」,不就是個這樣子的存在嗎?!
tkinter的按鈕元件語法中,當按鈕被按下時,要觸發的事件是透過command這個parameter來指定,寫法是:command = callback_function,也就是說,當按鈕按下時,會去執行callback_function。這看起來挺單純的,網路上許多教學文章都能看到說明。不過啊不過,網路上絕大多數的教學文章,都是一個按鈕配上一個callback_function,所以顯得很單純。但是現在是要用一大堆的按鈕來組成universe,這可不能聽從古聖先賢做人要甘願、要勤勞、不可以偷懶的教訓,真的埋頭苦幹,一個一個按鈕、一個一個callback_function的寫一大堆。那要怎麼寫?當然是用迴圈加上list來寫囉!而lambda function也就在這兒展現它的存在感。
雖然lambda function在這裡展現了它的存在感,不過這過程挺不乾脆的,遮遮掩掩、躲躲藏藏,頗費了一番功夫和時間才撥雲見日,把它給看個通透。
一開始還不知道要用lambda function,所以程式寫成這樣:
def change_state(btn):        
  btn['bg'] = 'red' if btn['bg']=='white' else 'white'
for i in range(10):
  btn[i] = tk.Button(root_window, bg='white',                                       command=change_state(btn[i]))
這段程式的目的,是產生10個白色的按鈕,當滑鼠點選按鈕時,那個被點選按鈕的顏色,就會由白色變為紅色,或由紅色變為白色。只是這樣子的寫法是錯的,在change_state中,會出現錯誤訊息:
TypeError: 'int' object is not subscriptable
想想也對,這btn[i]都還沒生出來,就把它當argument傳給change_state,如果這樣也可以,那就太神奇了。既然如此,那就等btn[i]生出來後,再來處理command,程式的迴圈部分就改成:
for i in range(10):
  btn[i] = tk.Button(root_window, bg='white')
  btn[i].configure(command=change_state(btn[i]))
改成這樣之後,錯誤訊息是消除了,不過生出來的所有按鈕,都是紅色的,而且不管怎麼點,顏色都不會變。
這到底是怎麼回事呢?實在是摸不著頭緒,後來想起曾在教學文章中有提到,可以用lambda function把callback_function的程式碼直接寫在command之後,而不用另外寫在def中,於是就把configure那行程式改成:
btn[i].configure(command=lambda: change_state(btn[i]))
天不從人願,這樣寫也不行。按鈕是生出來了,而且點選時顏色也會切換,可是不管點選哪個按鈕,切換顏色的,永遠是最後那個按鈕。
會不會是因為lambda function裡頭,呼叫change_state時,是用for迴圈的index來直接指定要改變狀態的是哪個按鈕,所以造成這樣的結果?改用parameter的寫法不知道行不行得通?試試看就知道!把lambda function改成這樣:
lambda n: change_state(btn[n])
結果不改還至少有個按鈕顏色會切換,改了之後,所有的按鈕都是白色的,不管怎麼按,就是不會變成紅色的。
唉!沒輒了,上網找答案。
原來,要這樣寫才對:
lambda n=i: change_state(btn[n])
這什麼語法啊,n=i?還有啊,那幾個錯誤的寫法,到底問題在哪?這樣子的疑問想要上網找解答,還真是考驗搜尋的功力。搜尋看似簡單,但要是不知道要用什麼關鍵字去找,那還真像是在荒郊野外找路一樣,充滿了天地茫茫,何方是歸處的感覺。
找了很久,試了很多關鍵字,每隔幾天想到新的方向、新的關鍵字就找一找,總算在stack overflow的討論串裡頭,看到了個比較清楚的解釋。好玩的是,那個討論串是在討論lambda function到底有什麼用,而不是在討論tkinter的按鈕寫法。難怪先前用tkinter相關的關鍵字搜尋,會找不到比較清楚完整的解釋,方向不對啊!
正確的寫法有了,那先前錯誤的寫法,到底錯在哪了?簡單來說,問題就出在對lambda function的運作方式不夠瞭解。
程式寫成lambda: change_state(btn[i])時,因為i是for迴圈的index,是在lambda function外面,是global variable,所以只有在lambda function被呼叫時,才會去存取i。在迴圈裡頭的程式碼,雖然造出了按鈕,但也就僅止於這樣,沒有去執行其他的什麼東西。而lambda function的部分,也就只是定義而已,要等到按鈕被按下,才會去執行。迴圈執行完建造按鈕的工作後,i的值是9,所以當按鈕被按下時,呼叫lambda function執行的,就是change_state(btn[9])。難怪不管按哪個按鈕,都是最後一個改變顏色。如果把lambda function改成lambda: change_state(btn[i-1]),那不管按哪個按鈕,都是倒數第二個改變顏色,可以進一步驗證這個解釋。
那後來把程式改成lambda n: change_state(btn[n])時,問題又出在哪?原來,因為n是lambda function的parameter,所以當按下按鈕時,n還是n,它一直就是n,不會變成argument。既然如此,不管按鈕怎麼按,根本就沒有任何argument傳給change_state,它只好把你的呼叫動作當空氣,已讀不回。
因為在定義lambda function時,並不會去存取global variable,所以在建造按鈕時,必須使用local variable來讓傳入change_state的按鈕,與當時迴圈index所指定的,是一致的。就因為是這樣,所以才會寫成lambda n=i: change_state(btn[n])。
呼!總算搞懂是怎麼回事了!可是,那個n=i又是怎麼回事?網路上找到的討論,都是直接就這麼寫、這麼用,沒有解釋。這個問題放在心中好一陣子,直到有天晚上心血來潮,想說在官網的文件找找看,說不定可以找到。
哈!還真的找到了!以lambda function為目標來找,在官網文件中的一些說明,不但解答了這個疑惑,連先前花了好多時間才在網路上找到解答的疑惑,都有很清楚的說明。這些文件包括:
  • Programming FAQ: Why do lambdas defined in a loop with different values all return the same result?
  • The Python Turorial: 4.8.6. Lambda Expressions
  • The Python Language Reference: 6.14. Lambdas
n=i這個語法的說明,就躲在第三個文件裡頭,而且還偽裝得挺好的,乍看之下沒察覺,直到後來才發現。怎麼說呢?文件裡頭是這麼寫的:
lambda_expr ::= "lambda" [parameter_list] ":" expression
然後在最後一段裡頭提到,那個parameter lists的語法,就參考8.7. Function definitions裡頭的說明。看到那段話,突然間醒悟過來:n=i不就是呼叫function時,keyward argument的寫法嗎?都說是lambda “function”了,在呼叫時,argument的寫法,不就該和function一樣?!真是的,怎麼就一直沒想到咧?!
其實那幾份文件先前都看過,只是那時就只是為了要知道Python有哪些東西而看,並不是為了尋求疑問的解答而看,所以就有如走馬看花,有看沒有到。為了解答心中的疑惑而看文件,就好像打開了主動雷達,原本被動接收,看來平凡無奇不是那麼重要的內容,就變成字字珠璣,充滿了深意。想想這尋覓瞭解lambda function的過程,還真是「衆裏尋他千百度,驀然回首……」像極了愛情啊~~~
後記:準備結束這篇lambda function的隨筆時,想到還有個疑問還沒解決,就是寫成btn[i].configure(command=change_state(btn[i]))時,為什麼生出來的所有按鈕,都是紅色的,而且不管怎麼點,顏色都不會變?上網查了一下,原因是因為在command=change_state(btn[i])中,change_state(btn[i])是keyward argument,所以實際上指定給command的,是執行change_state(btn[i])之後的傳回值。在建造按鈕時,會執行一次change_state。而因為change_state並沒有任何實際上的傳回值,它傳回的是None,所以最後得到的結果是command=None。這也就是為什麼所有的按鈕都是紅色的,而且點選時沒任何反應的原因。
為什麼會看到廣告
15會員
129內容數
寫點東西自娛娛人
留言0
查看全部
發表第一個留言支持創作者!
ysf的沙龍 的其他內容
唉!這user可真難伺候啊~~~
Game of Life的輸出結果,也就是每代演化後universe的長相,程式要怎麼寫呢?現在有個二維list,裡頭放的是一堆0和1,要怎麼樣用西洋棋盤式的方式來顯示呢?這看來勢必得用到別人寫好的module來做,才能省時、省力又漂亮。第一個想到的,當然就是matplotlib這個科學繪圖用的mo
如果「李大仁」長得像「武大郎」,而「程又青」長得像「東施」,這戲,還會有人看嗎?
Game of Life的「核心計算」部分寫好了,短短的沒幾行,畢竟也就那麼幾條判斷規則而已,沒什麼太複雜的東東要處理。說是寫好了,但到底能不能跑、跑出來的結果對不對,那可還是在未定之天哩。
在學習新事物的時候,如果能善用類比、聯想等小技巧,往往能事半功倍,但也是有可能會碰壁的。
適當的休息可以把陷進泥淖而不自知的心思給拉出來,從不同的角度和觀點來看問題,這樣說不定會發現另外不同的解決問題的方法。
唉!這user可真難伺候啊~~~
Game of Life的輸出結果,也就是每代演化後universe的長相,程式要怎麼寫呢?現在有個二維list,裡頭放的是一堆0和1,要怎麼樣用西洋棋盤式的方式來顯示呢?這看來勢必得用到別人寫好的module來做,才能省時、省力又漂亮。第一個想到的,當然就是matplotlib這個科學繪圖用的mo
如果「李大仁」長得像「武大郎」,而「程又青」長得像「東施」,這戲,還會有人看嗎?
Game of Life的「核心計算」部分寫好了,短短的沒幾行,畢竟也就那麼幾條判斷規則而已,沒什麼太複雜的東東要處理。說是寫好了,但到底能不能跑、跑出來的結果對不對,那可還是在未定之天哩。
在學習新事物的時候,如果能善用類比、聯想等小技巧,往往能事半功倍,但也是有可能會碰壁的。
適當的休息可以把陷進泥淖而不自知的心思給拉出來,從不同的角度和觀點來看問題,這樣說不定會發現另外不同的解決問題的方法。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
最近有一些新的覺察,邁入大學的生涯後,更加的明白,有很多的事情需要自己去體會、感受。才能找出屬於自己的最佳解。 大學就是一個小型的社會,多少時間是社交,又多少時間是留給自己,是一個流動的狀態,卻是大部分會給我帶來情緒波動的主因。 最近感悟到,當我在面對與自己完全不同的人時學到了如果對方沒有主動問
在棋盤的每一角落,棄子不僅是一種策略,更是一門藝術。
Thumbnail
不純粹的閱讀,你必然得到純粹的結論,例如看到討厭的同學發臉文,心裡開始品頭論足,最後得出他失戀失業的無能結論。嗯,或許討厭的同學只是單純實驗做失敗而已。 這年代了解不純粹的純粹很重要,還記得2024總統大選,有許多工程師小草放大自己的專業能力,覺得別人都很笨,用極大的意義解讀其他政治人物的動機?
Thumbnail
畢業季,離情依依,怕感情變疏遠!送別禮物特別能延續感情,但要送什麼好呢?讓友情歷久彌堅,分享好朋友送禮攻略!好書推薦「長輩沒教,但你一定要懂的81種送禮大學問」!
自從七月底陷入無止盡的鬱期之後 現在好像是進入躁期 毛毛躁躁的,不管是頭髮還是心理狀態(笑) 回到了無法看完一本書,一部影片的狀態 連文章都是打開了網頁,敲敲敲然後又再全部backspace. 有人問我說我的躁期會持續多久 因為長久以來我比較專注在鬱期的發展 所以沒有特別另外又去記錄自己躁期的階
Thumbnail
Yoga Retreat @ Greece  I do not know who need to hear this, but here it goes When you are blessed with a responsibility as a leader, teacher, coach,
Thumbnail
本文是2009/7/31號所寫,更新到方格子上。 有人說『成熟是一種智慧的成長』。 有人說『成熟是一個重生,靈性上的誕生,你煥然一新,又是個小孩子。』 有人說『成熟是會去改變自己。』 最後一句話我覺得很有道理,成熟的人會去改變自己而不是去努力改變環境。這些年的教訓
Thumbnail
對於簡報跟創業,曾經都以為跟自己很遙遠。也因此,能夠在這幾年,許多機緣引發的投入、際遇以及累積跟成長下,擔任首個華人新創簡報品牌、寶渥公司,舉辦本年度新創簡報競賽、PitchCamp2021的評審,不只是一個榮譽、也是一個肯定。這些機緣,絕大多數來自於BNI跟商戰經理人讀書會。
Thumbnail
Amazon製作了Invincible的動畫,第一季於前陣子上架。大概成果不錯,已經預訂了第二季跟第三季。 Invincible的漫畫跟動畫有甚麼不同,你可能想知道。
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
最近有一些新的覺察,邁入大學的生涯後,更加的明白,有很多的事情需要自己去體會、感受。才能找出屬於自己的最佳解。 大學就是一個小型的社會,多少時間是社交,又多少時間是留給自己,是一個流動的狀態,卻是大部分會給我帶來情緒波動的主因。 最近感悟到,當我在面對與自己完全不同的人時學到了如果對方沒有主動問
在棋盤的每一角落,棄子不僅是一種策略,更是一門藝術。
Thumbnail
不純粹的閱讀,你必然得到純粹的結論,例如看到討厭的同學發臉文,心裡開始品頭論足,最後得出他失戀失業的無能結論。嗯,或許討厭的同學只是單純實驗做失敗而已。 這年代了解不純粹的純粹很重要,還記得2024總統大選,有許多工程師小草放大自己的專業能力,覺得別人都很笨,用極大的意義解讀其他政治人物的動機?
Thumbnail
畢業季,離情依依,怕感情變疏遠!送別禮物特別能延續感情,但要送什麼好呢?讓友情歷久彌堅,分享好朋友送禮攻略!好書推薦「長輩沒教,但你一定要懂的81種送禮大學問」!
自從七月底陷入無止盡的鬱期之後 現在好像是進入躁期 毛毛躁躁的,不管是頭髮還是心理狀態(笑) 回到了無法看完一本書,一部影片的狀態 連文章都是打開了網頁,敲敲敲然後又再全部backspace. 有人問我說我的躁期會持續多久 因為長久以來我比較專注在鬱期的發展 所以沒有特別另外又去記錄自己躁期的階
Thumbnail
Yoga Retreat @ Greece  I do not know who need to hear this, but here it goes When you are blessed with a responsibility as a leader, teacher, coach,
Thumbnail
本文是2009/7/31號所寫,更新到方格子上。 有人說『成熟是一種智慧的成長』。 有人說『成熟是一個重生,靈性上的誕生,你煥然一新,又是個小孩子。』 有人說『成熟是會去改變自己。』 最後一句話我覺得很有道理,成熟的人會去改變自己而不是去努力改變環境。這些年的教訓
Thumbnail
對於簡報跟創業,曾經都以為跟自己很遙遠。也因此,能夠在這幾年,許多機緣引發的投入、際遇以及累積跟成長下,擔任首個華人新創簡報品牌、寶渥公司,舉辦本年度新創簡報競賽、PitchCamp2021的評審,不只是一個榮譽、也是一個肯定。這些機緣,絕大多數來自於BNI跟商戰經理人讀書會。
Thumbnail
Amazon製作了Invincible的動畫,第一季於前陣子上架。大概成果不錯,已經預訂了第二季跟第三季。 Invincible的漫畫跟動畫有甚麼不同,你可能想知道。