Python學習之變數、物件和引用

ARM的程式設計師敲著詩歌的夢發表於2019-03-09

Python學習之變數、物件和引用

問題的引出

當在Python中執行賦值語句a=3時,熟悉C語言的朋友肯定會感到奇怪:難道變數a不需要定義嗎?Python是怎麼知道變數a的型別的?

變數建立

一個變數(也就是變數名),就像a,當程式碼第一次給它賦值的時候就建立了它。之後的賦值會改變已建立的變數名的值。其實,說得更嚴格一點,這叫“名稱繫結”。

名稱用於指代物件(你可以理解為名稱是一個指向物件的指標)。名稱是通過名稱繫結操作來引入的。以下構造會繫結名稱:傳給函式的正式形參,import語句,類定義,函式定義,以識別符號為目標的賦值,for迴圈的開頭等等等等。

對於初學者,只要記住 以識別符號為目標的賦值會繫結名稱 就可以了。

變數型別

變數永遠不會有任何的和它關聯的型別資訊或約束。型別的概念是存在於物件中而不是變數名中。變數是通用的,它只是在一個特定的時間點,簡單地引用了一個特定的物件而已。

變數使用

當變數出現在表示式中時,它會馬上被當前引用的物件所代替,無論這個物件是什麼型別。此外,所有的變數必須在其使用前明確地賦值,使用未賦值的變數會產生錯誤。

動態型別

如果清楚地將變數名和物件劃分開來,動態型別是很容易理解的。例如:

>>> a = 3

對於上面的賦值操作,Python會執行三個不同的步驟去完成這個請求:

  1. 建立一個物件來代表值3
  2. 建立一個變數a,如果它還沒有被建立的話
  3. 將變數a與新的物件3相連線(link)

實際的效果如下圖:

在這裡插入圖片描述

Names and objects after running the assignment a = 3. Variable a becomes a reference to the object 3. Internally, the variable is really a pointer to the object’s memory space created by running the literal expression 3.

如圖所示,變數和物件儲存在記憶體中的不同部分,並通過連線相關聯(這個連線在圖中表示為一個箭頭)。變數總是連線到物件,並且絕不會連線到其他變數上,但是更大的物件可能連線到其他的物件(例如,一個列表物件能夠連線到它所包含的物件)。

在Python中從變數到物件的連線稱作引用(reference)。也就是說,引用是一種關係,類似於C語言的指標。

要點

  • 變數是一個系統表的元素,擁有指向物件的連線的空間。
  • 物件是分配的一塊記憶體,有足夠的空間去表示它所代表的值。
  • 引用是自動形成的從變數到物件的指標。

型別屬於物件,而不是變數

每一個物件都有兩個標準的頭部資訊:一個型別識別符號和一個引用計數器。型別識別符號用來標識這個物件的型別,引用計數器用來決定是否可以回收這個物件。

物件的垃圾收集

在Python中,每當一個變數名被賦值給了一個新物件,之前那個物件佔用的空間就會被回收(如果它沒有被其他的變數名或物件所引用的話)。這種自動回收物件空間的技術叫垃圾收集

在下面的例子中,每個語句都把變數名x賦值為不同的物件:

>>> x = 42
>>> x = 'hello'
>>> x = 3.14159
>>> x = [1,2,3]

首先注意x每次被設定為不同的物件。再者,儘管這不是真正的情況,效果卻是x的型別每次都在改變。

第二,每一次x被賦值給一個新的物件,Python都回收了舊物件的空間。例如,當執行x = 'hello'時,物件42馬上被回收(假設它沒有被其他的變數名或者物件所引用)。

在內部,Python是這樣來實現這一功能的:它在每個物件中保持了一個計數器,計數器記錄了當前指向該物件的引用的數目。一旦(並精確在同一時間)這個計數器被設定為0,這個物件的記憶體空間就會被自動回收。在前面的介紹中,假設每次x都被賦值給一個新的物件,而前面的一個物件的引用計數器變為0,就會導致它的的空間被回收。

垃圾收集最直接的好處就是可以在程式碼中任意使用物件而不需要考慮釋放記憶體空間。與C語言這樣的底層語言相比,真是給程式設計師省了不少麻煩。

【End】







參考資料

《Python學習手冊(第4版)》,機械工業出版社

相關文章