3. 資料模型¶
3.1. 物件、數值和型別¶
物件 是 Python 為資料的抽象表示方式。一個 Python 程式當中的所有資料皆由物件或物件之間的關係來呈現。程式碼也都是以物件呈現的。
每個物件都有一個識別性、型別,和數值。物件的識別性在物件建立後永遠不會改變;你也可以把它想成是該物件在記憶體中的位址。is 運算子會比較兩個物件的識別性是否相同;id() 函式則會回傳代表一個該物件的識別性的整數。
在 CPython 當中,id(x) 就是 x 所儲存在的記憶體位址。
一個物件的型別決定了該物件所支援的操作(例如「它有長度嗎?」),也同時定義該型別的物件能夠擁有的數值。type() 函式會回傳一個物件的型別(而該型別本身也是一個物件)。如同它的識別性,一個物件的型別 (type) 也是不可變的。[1]
某些物件的數值可被改變,這種物件稱作「可變的」(mutable);建立後數值不能變更的物件則稱作「不可變的」(immutable)。(不可變的容器物件中如果包含對於可變物件的參照,則後者的數值改變的時候前者的數值也會跟著一起改變;這種時候該容器仍會被當成是不可變的,因為它包含的物件集合仍然無法變更。因此可變或不可變嚴格說起並不等同於數值是否能被改變,它的定義有其他不明顯的細節。)一個物件是否為可變取決於它的型別;舉例來說,數字、字串和 tuple 是不可變的,而字典與串列則是可變的。
物件永遠不會被明示的摧毀;但當它們變得不再能夠存取的時候可能會被作為垃圾回收。每個實作都能延後垃圾回收或是乾脆忽略它 --- 垃圾回收如何進行完全取決於各個實作,只要沒有被回收的物件仍是可達的。
CPython 目前使用一種參照計數的方案,並提供可選的循環連結垃圾延遲偵測,這個方案會在大部分物件變得不可存取時馬上回收它們,但不保證能夠回收包含循環參照的垃圾。關於控制循環垃圾回收的資訊請見 gc 模組的說明文件。其他實作的行為不會相同,CPython 也有可能改變,因此請不要仰賴物件在變得不可存取時能夠馬上被最終化(亦即你應該總是明確關閉檔案)。
請注意,使用一個實作的追蹤或除錯工具可能會讓原本能夠回收的物件被維持存活。也請注意,使用 try...except 陳述式來抓捕例外也可能會讓物件維持存活。
某些物件包含對於「外部」資源的參照,像是開啟的檔案或是視窗。基本上這些資源會在物件被回收時釋放,但因為垃圾回收不保證會發生,這種物件也會提供明確釋放外部資源的方式 --- 通常是 close() method。強烈建議各個程式明確關閉這種物件。try...finally 陳述式與 with 陳述式提供進行明確關閉的方便手段。
某些物件包含對於其他物件的參照;這種物件被叫做「容器」。容器的範例有 tuple、串列與字典。這些參照是容器的數值的一部分。通常當我們提到容器的數值的時候,我們指的是其中包含的物件的數值,而不是它們的識別性;但當我們提到容器是否可變的時候,我們指的是直接包含在其中的物件的識別性。因此,如果一個不可變的容器(像一個 tuple)包含對於可變物件的參照,該可變物件被變更時該容器的數值也會跟著變更。
型別幾乎影響物件行為的所有面向。就連物件識別性的重要性某種程度上也受型別影響:對於不可變的型別,計算新數值的操作可能其實會回傳一個某個相同型別且相同數值的現存物件的參照;對於可變型別這則不會發生。舉例來說,在進行 a = 1; b = 1 之後,a 和 b 可能會參照同一個物件,也可能不會,取決於所使用的實作。這是因為