這本書的網址放在瀏覽器的書籤中已經有段時間了,但因為書中用來實作的程式語言是Processing p5.js (2024年改版後改用p5.js),而現在正在學的是Python,所以也就一直沒花時間去讀。寫完Stochastic L-system之後,因為還沒決定接下來要寫些什麼,有天百無聊賴地翻著瀏覽器中的書籤,看看有沒有什麼先前收藏,但沒去看的好東西,可以用來打發時間。看到這本書的連結書籤時,想想反正閒著也是閒著,那就看看吧。
這一看不得了,很多想寫的東西的理論觀念和程式寫法,在這本書裡頭都有介紹,而先前寫過的fractal、Game of Life、L-system等,也都包括在其中。既然如此,那就把它看完吧!
這本書用來實作的程式語言是Processing p5.js,不是Python,所以在看的過程中,主要還是著重在理論和觀念部分,程式部分就只是看過去,沒有實際去練習書中的範例和習題。有天看著看著,突然冒出個主意:何不用Python來實作這些範例和習題?這可真是個好主意!這樣既可學到書中介紹的理論、觀念,又可以練習Python。於是決定,把整本書大略看過之後,就從頭再看一遍,邊看邊寫Python程式。另外,還要把心得寫下來,分享到網路上,跟網友互相漏氣求進步。
寫心得的這個決定是對的,就是因為要寫,才發現本來以為自己瞭解的東西,原來並非如此。「教學相長」是個老掉牙的說法,但只要有過認真想「教」好的經驗,絕對會認同這樣的說法,因為除非自己真的完全理解了、學會了,否則是很難教得好的。好的老師,可以讓學生學到東西;厲害的老師,可以讓學生在學習的時候,打從心底浮現「我懂了!我會了!」的豁然開朗笑容;而頂尖的老師,則是會讓學生翻白眼心裡直犯嘀咕:「這麼簡單!我來教都比你教得好!」寫心得與分享,其實就是在教未來的自己與看文章的人,這是檢驗自己是不是真的懂了的最好方法。
心得寫到「Introduction」 「Randomness」這一章的第二節「I.2 0.2 The Random Walker Class」就卡住了,關鍵在這一句話
A class is the template for building actual instances of objects.
這句話是在解釋物件導向程式設計(object-oriented programming, OOP)中的class是什麼。先前看的時候覺的這句話很正常,這就是class的定義,沒啥奇怪的。可是,要寫下心得的時候,卻發現不對勁。印象中,這instance和object不是一樣的東西嗎?怎麼會有「instances of objects」這樣的說法出現?
要寫出來的東西,如果連自己都有疑問,那可不行,非得搞清楚不可,不然寫不下去。
天啊!怎麼這麼混亂!網路上有一卡車的文章在談class、object、instance有什麼不同;在Stack Overflow中,關於object和instance間的差異,也一再有人問起。只是啊只是,看了一大堆的討論、解釋,似乎是懂了,但又總覺得不踏實,就好像漂蕩在太空中,明明目標就在眼前,但無處施力,怎麼也搆不著。
這問題擱在心中好幾天,各種觀點和解釋也不時出沒在腦海中,直到想起書櫃中還有「世紀末軟體革命」這本書。重新再看了一下書中關於這方面的解釋,經過再三的推敲、琢磨,總算踏踏實實地覺得自己懂了。
先來看看查到的資料說些什麼:
- class是製造object用的藍圖
- class是描述object細節的藍圖或模板
- class是用來定義object的一種東西
- class就是某一類的object,它定義了其內不同object的共通性質
- object是class的instance,可以把object和instance看作是同樣的東西
- object是class的複製品,而instance是保有object記憶體位址的變數
- object是class在記憶體中的physical presence
- object就是那個使用new來instantiate一個class之後,所產生的那個東西
- object是一種self-contained entity,具有資料與用來操作該資料的程序
- instance是特定的object
- instance是在記憶體中的變數,其內只有存放object的記憶體位址
- instance就只意味著產生一個reference (copy)
- instance是object獨一無二,具有相同結構與不同資料的複製品
- instance就是賦予object的資料,也可稱之為object的狀態
不知各位看倌看了這些說法之後,感覺如何?這些說法單獨來看,似乎都言之成理,可是有些說法之間,卻是互相矛盾的。例如,有些認為object和instance是一樣的東西;有些卻認為先有object,然後再產生instance。怎會這樣呢?
再溫習了一次「世紀末軟體革命」之後,總算能對上述這種混亂的局面理出個頭緒來。原來,這一切都是因為切入點不同所引起的,有些說法是由OOP的理論架構切入;另有些是由程式語言的運作機制切入,而引起混亂的源頭,似乎就是「instance」這個英文字。
想要搞懂class、object、instance這三者間的關係,先從理論架構的視角來看,然後再從實際寫程式時的角度來看,這樣會比較清楚一些。
在現實世界中,人們會把東西分類。例如,那種有四條腿、看到你會搖尾巴、會汪汪叫的東西,人們會把牠們歸為同一類,然後管這類東西叫「狗」。之後當有人說他養了隻叫「開心」的「狗」時,別人雖然沒見過「開心」,但也知道「開心」有四條腿、會對著你搖尾巴、會汪汪叫。以OOP的術語來說,就是人們把有四條腿、看到你會搖尾巴、會汪汪叫的object,歸類為「狗」這個class。這麼一歸類之後,很顯然的,「開心」就是「狗」這個class的一個實際上的例子,也就是instance。
另外還有一種情形,就是人們根據設計圖製作東西。以OOP的角度來看,設計圖就是class,裡頭規範了做出來的東西,也就是object,該是什麼樣子。
上面這兩種過程看起來似乎是相反的,一個是把已存在的object歸入相同的class中;另一個是依據class來製作object。但其實,它們只不過是一個完整過程,掐頭去尾之後的結果。那完整的過程長怎樣呢?完整的過程,就是人們把有四條腿、看到你會搖尾巴、會汪汪叫的object,歸類為「狗」這個class之後,依據「狗」這個class裡頭有的特性,製作出「狗」這個object。
雖然完整的過程是那個樣子,但在OOP裡頭能看到的,就只是其中一部份。當人們使用OOP寫程式時,會先設計class,然後製作object。這個設計class的動作,其實只是看得到的部分,看不到的部分,是腦袋瓜在思考、決定,什麼樣的特性該歸類到這個class裡頭的過程。這個看不到,決定class該長什麼樣子的部分,其實是一切的根基。class設計得不好,就像房子的地基不穩一樣,很難補救的。
從上面的分析,在OOP裡頭,class和object之間的關係就很清楚了。那instance呢?它和class、object間的關係又是怎樣的?
要想搞清楚instance到底是個怎麼樣的存在,就必須搞清楚,到底是class還是object,衍生出instance這麼個讓人混亂的東西來?
看到instance會想到什麼?
蛤?!它不就是個英文字,還能是什麼?
沒錯!instance是個英文字,但這個英文字的意思是什麼?查一下字典,可以知道instance這個字的意思是
- an example or single occurrence of something
- a particular case
從instance這個英文字的意思,就可以看得出來,為什麼要用它來描述class和object間的關係,也就是:object是class的instance。而從這樣的關係中,也就能得到「可以把object和instance看作是同樣的東西」這樣子的結論。
既然說object和instance可以看成是一樣的東西,那為什麼又會有「instances of objects」這樣子的說法呢?這樣子不就代表instance是從object生出來,屬於object的嗎?其實這麼說也沒錯,instance這個字,本來就是這個意思。反正一堆東西中的某一個,就是這堆東西的一個instance。之所以會讓人搞不清它是什麼,主要是因為把instance當成了是OOP裡頭,像class、object一樣,有嚴格定義的東西了。所以當看到像「instances of objects」這樣的詞句時,才會疑惑,在談class、object時,不是說instance跟object是同樣的東西嗎,怎麼現在看起來又好像是另一種東西了?
總之,面對一個名詞時,不要先入為主的,就以為是字面上的意思,而應該先搞清楚,人家有沒有事先給了明確的定義。如果沒有明確的定義,就把它當成一般的名詞,根據上下文來理解它的含意。就如同有些書或文件,當談到OOP時,會很明確地說,會把instance和object當成是一樣的東西,而這兩個字也會交替使用。這時候,不用想太多,就把它們當成是一樣的東西來看就可以了。但是啊但是,在有些書中或文件中,並沒有這麼的特別去定義instance是什麼,這時候就必須頭腦清醒地根據上下文來判斷了,Python官網的文件就屬於這種情形。
在Python官網的Glossary文件中,並沒有定義instance是什麼。而在Tutorial的Classes章節中,可以看到像下列這些詞句
- class instance
- Many classes like to create objects with instances customized to a specific initial state.
- instance objects
- class instance objects
- instances of that class
這時候,就只能靠上下文來理解instance指的是什麼,而不能一股腦兒的,就把instance和object當成是一樣的東西,否則只會越看越混亂。
在Python中,讓人混亂的不只instance而已,object是另一個狠角色。
在Python中,object這個字,除了原本的翻譯成「物件」的意思之外,所有class的祖先,也是用object這個字。換句話說,object也可能是指那個特定的class。還有啊,因為萬事萬物在Python中,都是「物件」。所以囉,看到class object、instance object、list object、module object、file object、function object、method object等什麼什麼object時,可別太訝異。
接下來,就來看看實際的程式寫法。透過程式,或許會比較能夠理解,前面那一大堆網路上查到的,關於class、object、instance的看法,究竟在說些什麼。
前面提到過「開心」這隻狗,就讓牠來當程式的主角好了。假設我們已經設計好Dog這個class了,接下來,就來看看用Java和Python要怎麼寫,才可以讓「開心」生出來,而且幫牠綁上一條掛有「happy」牌子的狗繩,以免牠走失。
Java的寫法:
Dog happy;
happy = new Dog('開心');
也可以寫成
Dog happy = new Dog('開心');
Python的寫法:
happy = Dog('開心')
當用Dog這個class生出「開心」時,系統會配置記憶體給這個object。至於happy,並非object本身,而是一個指向那個object的reference。從這裡也可以看出,網路上查到的那一大堆解釋,有些有它的道理在,但有些可能就不是那麼的正確了。
過了一年,「開心」長大了,已經一歲了。這時,程式該怎麼寫呢?假設在設計Dog這個class的時候,關於年齡的部分已經考慮進去了,這時候要讓「開心」長大成一歲,Python的寫法是:
happy.age = 1
隨著歲月的流逝,「開心」的年齡也跟著繼續增長,也就是
happy.age = 2
happy.age = 3
...
「開心」還是同一個object,但age不一樣。所以,不同年齡的「開心」,不就是happy這個reference所指的object的instance嗎?既然這樣,那「instances of an object」或「instances of objects」的意思,也就很清楚了。
有陣子常在想,退休之後就來養隻狗、養隻貓,外加一隻老鼠,然後每天就貓抓老鼠狗追貓,這該多麼有趣啊!當然啦,這只能想想,現實生活中是不太可能發生的。現實是,當發現自己又在做著像中大樂透或貓抓老鼠狗追貓這樣子的白日夢時,就知道該舒壓了。不過話又說回來,雖然不能真的在家裡貓抓老鼠狗追貓,那在電腦中模擬總該辦得到吧?
要想在電腦中模擬貓抓老鼠狗追貓,第一步要做的,當然就是要先設計出那三個傢伙。然後……咦?這個「追」是怎麼個追法?是天敵見面分外眼紅,齜牙咧嘴的追,還是吐著舌頭,流著冒著粉紅泡泡口水,眼神充滿對異性愛慕之情的追?
看來又遇上一個複雜的三角關係了……