在武俠世界中,各門各派都有自己獨門的絕學。其實程式語言也一樣,每種程式語言也都有自己的一套。當然囉,武功絕學得好好保護,不可輕易外傳或被偷學;而程式語言呢,則是大家互相觀摩,互相漏氣求進步。所以啊,武功絕學就一個一個失傳,而程式語言則是互相吸取優點,越來越進步。
comprehension應該可說是Python的絕學之一吧。不過既然是絕學,總是會有讓人容易在運氣時一個不小心走錯經脈的地方。現在我需要一個二維的list,利用comprehension來造一個,應該是再好不過的選擇。只是這地方,就是個容易出錯的地方。
一開始是這麼寫的:
universe = [[0 for x in range(width)] for y in range(height)]
這個寫法ok,只是覺得有點囉唆。後來想到這樣寫:
universe = [[0]*width]*height
嘿!清爽多了,不是嗎?實際執行看看,的確印出來的,就是個塞滿0的二維list。這時候還真是有點佩服自己,能把原來囉哩囉唆的寫法,給改寫成這麼簡潔的寫法。
在繼續往下寫之前,想到好像在官網的文件上,曾經看到有關多維list的主題。會不會有更好的寫法?嗯!應該先看看再繼續。果然!在Programming FAQ裡頭,就有一個主題是在講如何造多維list。
呃……好吧!上面比較短的寫法是錯的。因為用「*」來複製list所得到的,是指向現存物件的reference,而不是真正的list物件。所以啊,「*height」所造出來的,是一個list,這個list含有height個reference,而這些reference都指向同一個長度為width的list。所以當改變universe裡頭某列元素的值時,每一列相同位置的元素都會被改掉。所以正確的寫法應該長這樣:
universe = [[0]*width for y in range(height)]
在其他網站看到,在迴圈部分,也可以寫成這樣:
universe = [[0]*width for _ in range(height)]
這個寫法倒沒什麼太特別的技術原因,純粹就是寫作風格而已。底線(underscore)代表的是:這個東東的值沒啥重要的,在迴圈裡頭也不會用到,不值得在意。既然這樣,那就用底線代替y,省得以後看程式時,會懷疑y是真的沒用到,還是寫錯、漏掉了。這主意不錯!可以避免一些麻煩,也讓程式讀起來更明確。寫程式時,如果能多考慮後續維護、除錯時的問題,就能節省很多時間,也能避免很多錯誤。就像地下管線要留人孔,方便維修;電線的地線、火線顏色不同,方便辨識一樣,寫程式時多費些心思,絕對是好處多多!