Python特色簡述-變數與記憶體管理

  • 10604
  • 0

摘要:Python特色簡述-變數與記憶體管理

Python是一個專注於易讀性與一致性的程式語言。
小弟將最近學習Python所感受到的一些Python的特色紀錄於下。

Python的主要變數型態有:number string list tuple dictionary
其中number list tuple是不可變更的,dictionary的key也是不可變更的(但可新增)。
雖說這些變數不可變更,我們仍可透過指定新值的方式去變更。
而tuple是list不可變更的版本,若是希望程式的list不要在執行過程中變更,一開始可宣告為tuple
list tuple dictory都可任意巢狀化。

為何list內可以不限定資料型態,且可自由巢狀化?
這是因為Python的變數事實上不是存放物件本身,而只是Reference,類似C語言的指標,指向物件所在之記憶體位置。當指定新值給變數時,就是改變Reference指向新的物件。
因此當list中某一欄的指標指向新的list,就成為巢狀結構了。
舉個例子來說

a = [3, ['Spam', 'Egg'] ,( ['Brian'],42,'Python'), 5]

在記憶體裡面的示意途大致如下

在Python中,物件是有型態的,而變數名稱只是物件的Reference。
沒有Reference所指及的物件,將會被回收機制給收回。
id()是一個看到變數Reference的function,is可以判斷是否為相同物件
以下用這兩個功能來示範。

>>> a = "Spam"
>>> b = a
>>> id(a) #a所代表的Reference
13458464
>>> id(b)
13458464
>>> a is b #a是否與b指及相同物件
True
>>> b = "Egg" #將b指向新的物件
>>> id(a)
13458464
>>> id(b)
13458272
>>> a is b
False

示意圖(虛線為後來指定的值):

由於string是不可變更的,因此當指到新的物件時,會重新指定Reference
而list由於是Reference的陣列,內容的Reference是可以變更的。

>>> a = ["Spam"]
>>> b = a
>>> id(a) #變數a的Reference
13442824
>>> id(a[0]) #變數a的陣列內容index為0之Reference
13458464
>>> id(b)
13442824
>>> id(b[0])
13458464
>>> a is b
True
>>> a[0] is b[0]
True
>>> a is a[0]
False
>>> a[0] = "Egg" # 變數a的Reference沒有變更,而是變更陣列表上index為0之Reference
>>> b
['Egg']
>>> id(a)
13442824
>>> id(a[0])
13458208

示意圖:

如果要避免這種狀況,可以用[:]或是copy功能來複製一個list

>>> a = ["Spam"]
>>> b = a[:]
>>> id(a)
13425280
>>> id(a[0])
13418368
>>> id(b)
13424280
>>> id(b[0])
13418368
>>> a is b
False
>>> a[0] is b[0]
True
>>> b[0] = "Egg"
>>> id(b)
13424280
>>> id(b[0])
13455456
>>> a
['Spam']

使用copy功能

>>> import copy
>>> a = ["Spam"]
>>> b = copy.copy(a)
>>> b
['Spam']
>>> id(a)
13442824
>>> id(b)
13470816
>>> a is b
False
>>> a == b #雖然為不同物件,不過值是相同的。
True

示意圖:

另外Python針對小的number與string會保留物件並指到相同物件

>>> a = "Hello"
>>> b = "Hello"
>>> a is b
True
>>> import sys
>>> sys.getrefcount("Hello") #可以看到目前有多少Reference指向"Hello"
4
>>> b = "Tom"
>>> sys.getrefcount("Hello")
3

從以上範例可知,Python有自己一套處理記憶體的方式,藉此減少記憶體使用,同時也增加變數在指派的彈性。

參考資料:

Python學習手冊‧第三版