經驗是一把兩面刃。有了過去的經驗,在學新的東西時,可以觸類旁通,很快的進入狀況;而在面對從未遇過的狀況時,也可以快速做出反應,以爭取時效,或者規避危險。學會騎腳踏車後,再來學騎摩托車,可以很快就學會,這是經驗的正面效果。那經驗的負面效果呢?「一朝被蛇咬,十年怕井繩」就是個典型的例子。或許是生存的本能,我們總是會因為不好的經驗,而去規避,不願接受挑戰,最後躲在舒適圈不肯出來。另一種經驗的負面效果是,太相信過去成功的經驗,導致墨守成規,僵固於過去的模式,而以錯誤的或過時的方式,來應對處理全新的狀況,最後以失敗收場。在商業上,這種例子屢見不鮮;在程式語言的學習上,也是一樣。過去學習過的程式語言,可以讓你在學習新的語言時,很快就能進入狀況,畢竟很多觀念跟結構是共通的,例如條件判斷、迴圈、data type、class、object... 但是,每個語言也有其不同的設計,稍微不注意,就很容易用過去學過的語言來理解,而忽略了其內涵的不同之處,導致多走了許多冤枉路。
在Python中,所有東西都是物件。執行程式碼a = 5,會建立5這個物件,然後給a一個reference,這個reference就是告訴a,它的值放在記憶體的哪個位置,要用時,就到那裡去拿。
看到這裡,很直覺的反應是:這不就是C裡頭的pointer嗎?!然後就沒再多想了。
immutable object如int、float、complex、string、tuple等型態的object,他們的值是固定的,不能改變;而mutable object如list、dict、set等型態的object,他們的值是可以改變的。
昏頭了!什麼immutable、mutable?為什麼不說unchangeable、changeable?意思不都一樣嗎?查了網路上的字典,都還是同義字咧!有的字典還乾脆就說immutable、mutable是程式語言用語,然後解釋了一番,可我的疑問是,為什麼當初要選這兩個字來用,而不是用另外有同樣意思的字眼?好吧!這可能很難考證了,畢竟新的東西不斷出現,總得有個字眼來指稱,有時候還真沒什麼太大道理可言,就好像ball point pen翻成中文時,硬是叫成「原子筆」,只因那時「原子」兩個字很潮、很厲害。還有啊,讓人充滿遐想的「比基尼」,其實是因為剛推出時,美國正好在「比基尼環礁」進行核試爆,取這個名字容易吸引人,於是就這麼用了。所以啊,immutable就immutable;mutable就mutable,反正就是那意思,現在不也一堆東西叫「量子」什麼東東的,也沒什麼道理啊!還有還有,現在電動車越來越普及,未來燃油車應該會走入歷史。現在用來加速的那個,我們管它叫「油門」。可是電動車用的是電,不是油。那你說那個用來加速的,該叫什麼?好像大家還是叫它「油門」。未來的某一天,或許會有人開始探究,車子明明就沒有用油當燃料,為什麼要叫「油門」咧?!
immutable object的值不能改變,如果寫a = 5,表示a的型態是int,所以值不能改變。可是可以接著寫a = 6,這時候a的值就從5變成6了,這樣怎能說是immutable?原來,執行a = 5時,會給a一個reference指向5,再執行a = 6時,也就只是給a另一個reference指向6而已。看起來挺合乎邏輯的,不是嗎?immutable這觀念是懂了,可又覺得有點彆扭,但也說不出哪裡不對勁。
倪匡筆下的衛斯理在面對問題時,常常會有那種似乎彷彿好像抓住了什麼解決之道,但又說不出個所以然來的情形。然後,在後來的某一瞬間,一切都豁然開朗,答案清晰可見。其實這種現象倒也不是小說中才會出現,現實生活中也常見。就在和巢狀list的複製問題奮戰的時候,突然間,就像剔掉塞在牙縫中很久的菜渣一樣,一陣輕鬆,神清氣爽,immutable算是真正搞懂了。原來,immutable object指的是像5、6這些數字,不是變數a,在許多解釋immutable、mutable觀念的文章中,其實都有提到,只是並沒有特別去強調而已。先前因為把reference類比為pointer,所以沒去特別注意,才導致後來在巢狀list的複製問題上卡關。
在其他需要宣告變數的程式語言,例如C、Fortran、VB中,宣告變數,例如int a,是讓系統配置一塊記憶體給a這個變數。這塊記憶體有多大呢?就int這麼大!同樣是整數,配置的記憶體大小,不同的程式語言在不同的作業系統上,可能都不同。但無論如何,反正就是會照程式語言的規定給就是了。那變數a要這塊記憶體做什麼?就把它當倉庫而已!這倉庫裡頭放的,就是a的值。不過雖然是倉庫,也不能什麼東西都塞進去。既然當初宣告時,就已經表明,倉庫裡頭是要擺放int這個型態的數字,那做人要甘願,更何況是白紙黑字寫下的宣告,這個倉庫裡頭,就只會放int型態的數字。換句話說,a的值,就一定是int型態的數字。當然囉,就像房子會有地址一樣,a的倉庫,也就是配置的記憶體,也有地址,這樣才知道東西存放的確切地點,而pointer指的就是這個地址。當要改變a的值的時候,直接把新的值放到a的倉庫就好了,a還是a,地址也沒變。所以啊,其實a和它的倉庫的地址是一體兩面,只是在寫程式的時候,用a比較簡單、方便,畢竟倉庫的地址可是一大串令人頭皮發麻的數字。上網時,我們會用網址,而不是用ip,也是同樣的道理。
說來有趣,address這個字,就是「地址」的意思。可是在資訊領域,卻會翻成「位址」。「位址」兩個字感覺就是比較有學問一點,不過仔細想想,這樣翻還挺有道理的,畢竟如果是要指稱在記憶體中的位置,「位址」還是比較傳神一點。
就是因為根據以前的經驗,宣告變數以後,例如int a,就有了a這個型態是int的變數,不管是後來寫a = 5,或直接把5放到a的記憶體倉庫中,乃至於複製a的值給其他變數,只要有a在心中就可以了,pointer其實可以不用太在意,畢竟沒那玩意兒的Fortran、VB也活得好好的,不是嗎?所以啊,在Python裡頭看到a = 5時,自然而然的,心中也就只有a了。至於reference,想當然耳,就是pointer嘛!也就也沒太在意了。這樣子的認知,其實也沒什麼太大的問題產生,等到面對list這種mutable object時,才覺得這邊也卡卡的,那邊也卡卡的,直到徹底拋棄pointer,重新認識reference為止。在武俠世界中,很多絕世武功,都必須散盡全身功力,徹底告別以前苦練的成果,才能練成,或許也就是這麼回事吧!