標籤:
作為一門動態語言,python很重要的一個概念就是動態類型,即對象的類型和記憶體佔用都是運行時確定的。(Why?)運行時,解譯器會根據文法和右運算元來決定新對象的類型。
動態類型的實現,是通過引用和對象的分離達到的。對象是存放在記憶體中的資料實體,而引用(reference)可以理解成一個封裝好的指向對象的指標。不過操作更加方便和安全。就像C++中的引用是對指標操作的簡化和封裝一樣。在python中,記憶體的管理,即分配與回收,都是由python解譯器來做的,程式員不需要關心太多。或者,也可以把引用理解成是對象的一個別名,一個對象可以有多個別名都指向它。比如後面的代碼中的3.14這個浮點對象,x 和 y 都是指向它這個對象的,都可以看做是它的別名。就像一個人可以大名叫張某某,而小名叫張三一樣。
在python中,建立一個對象時,解譯器負責記憶體的分配,同時內部一個叫做引用計數器的東西在對象被賦值給變數時,置為1。當把對象賦值給變數時,是把對象的引用賦給了變數。當再次將這個對象賦值給其他變數時,引用計數器+1。如:
x = 3.14y = x
第一行代碼中,建立了一個浮點型的對象,同時,將其引用賦值給了一個名為x的變數。這是引用計數器的值為1。接下來,將 x 賦值給 y ,即再次將這個浮點對象的引用賦值給y,同時引用計數器+1, 值為2。也就是說,當把x賦值給 y 是並沒有建立新的對象。記憶體中的資料對象依舊只有一個,但是有兩個變數x , y 都指向這同一個對象。這一點,和C/C++是完全不一樣的。但是呢,就像前文中說的,和C/C++中的指標有點像。如:
int tmp = 10;int *p = &tmp;
上述代碼中,先定義了一個int型變數tmp,並初始化為10,接下來,定義了一個指標p指向tmp對象的記憶體。也就是說,tmp和*p指向的是同一個記憶體對象。不過,python做了底層的工作,我們只需要像操作普通變數那樣操作就可以得到類似指標的效果了。
回到python,當我改變 x,y 中的 其中一個的值時,另一個是不受影響的。如:
x = 3.14y = xx = “A”
這時,如果進行輸出,x 的值為5, 而 y 的值依舊為 3.14 。因為當執行x = “A” 語句時,其實是建立了一個新的對象,並把其引用賦值給了 x 。這時,x 和 y 已經是兩個不相關的兩個變數了。因為它們指向了不同的對象。在執行這個語句的同時,y 所執行的 3.14這個浮點數對象的引用計數器減一。當引用計數器的值為零,即沒有變數指向這個對象時,系統會自動銷毀這個對象並回收記憶體。(當然,實現上來說,並不一定是引用計數器為0了,就馬上回收,可能會有一些回收的策略。) 此外,可以看到的是,一開始 x 指向的是一個浮點型對象,而現在卻指向了一個字串。因為在 Python 中,變數名僅僅與是指向對象,而與對象的類型是無關的。這裡涉及到弱類型,強型別,就不涉及更多了,因為不太懂。(Right?)
回到引用計數,在以下4種情況下,引用計數器增加:
1> 對象被建立
x = 3.14
2> 或另外的別名被建立
y = x
3> 或作為參數傳遞給函數(新的本地引用)
foobar(x)
4> 或稱為容器物件的一個元素 (容器?)
myList = [123, x, ‘xyz’]
而在以下情況下,引用計數器減少:
1> 一個本地引用離開了其作用範圍,比如foobar()函數結束時。
2> 對象的別名被顯示銷毀
del y
3> 對象的別名被賦給其他對象
x = 123
4> 對象從視窗對象中移除
myList.remove(x)
5> 視窗對象本身被銷毀
del myList
del 語句會刪除對象的一個引用。它有兩個效果:
1> 從現在的名稱空間中刪除變數名
2> 對象的引用計數-1
轉載請註明地址: http://www.qyspaces.com/?p=272
Python記憶體管理及引用計數