1. 程式人生 > >Python中類變數與成員變數相互影響的原因超詳細解釋

Python中類變數與成員變數相互影響的原因超詳細解釋

Python類變數與成員變數相互影響的原因超詳細解釋

講的是類變數和成員變數的影響原因,但原文並沒有講明詳細原因,只是講清楚了類變數與成員變數影響的結論,由於前幾日看了《Python學習手冊》這本書瞭解了一下Python的資料儲存機制,然後自己想並且試驗了一下,大致找到了原因,由於作者水平有限如原因有錯請多原諒並指出,感激不盡。

先科普一下Python的資料儲存機制,知道的就跳過往下看吧,Python的變數不用宣告且可以隨意賦值,因為Python的變數其實是類似C語言中指標的包裝,例如python中

a=3

-------------------------------------------------------------------------------------------------------------------------------------------------

所以在python中變數皆為指標。那麼,看看原文章中的例子

class A:
    x = []
    y = 0

    def __init__(self):
        pass

    def add(self):
        self.x.append('1')
        self.y+=1
a=A() 
print a.x,a.y
print A.x,A.y

a.add()
print a.x,a.y
print A.x,A.y

b=A() 
print b.x,b.y
print A.x,A.y

輸出結果為

  •  [] 0
  •  [] 0
  •  ['1']1
  •  ['1']
    0
  •  ['1']0
  •  ['1']0

-------------------------------------------------------------------------------------------------------------------------------------------------

先來從頭到尾分析一遍:因為首先生成a這個物件並沒有例項變數(__init__沒有引數),所以雖然a例項和A類為兩個不同記憶體地址,但是a.x和A.x卻都是引用類A變數x的記憶體地址(類建立的時候一塊記憶體地址出現了[]這個值,由於後續生成例項並沒有賦值操作,所以整個記憶體中有且僅有一個[]值,又因為變數為指標,所以這個兩個變數同時指向[]的記憶體地址)

所以當a呼叫a.add方法時,self傳的又是自身,相當於執行了a.x.append('1'),而a.x與A.x指向的記憶體相同,由於a.append函式並不改變變數a指向的記憶體地址,而是改變記憶體地址中的值(相當於在a所指的記憶體地址中的原值進行操作即對[]進行操作)

a=[]
print(id(a))
a.append('a')
print(id(a))

輸出結果為兩個相同的值,即append函式是對原值進行了操作。所以A類物件本身值被改變,以至於接下來生成的b例項的b.x值也被改變,那麼為什麼A.y這個值卻沒有改變呢?

-------------------------------------------------------------------------------------------------------------------------------------------------

下面分析y這個類變數,給你們下面這段程式碼你們應該就懂了

i = 1
print(id(i))
i +=1
print(id(i))
輸出結果是兩個不同的值    原理是假設1這個值被存放在0xF0這個記憶體空間,進行i+=1的時候,1+1=2,2這個值生成並被放在假設0xFF這個地址內,然後i從指向0xF0轉為指向0xFF,而不是2這個值覆蓋原地址。

根據這個結論來討論我們的問題a.add()的函式進行了self.y+=1的操作,雖然self.y 和 a.y 和 A.y為同一個記憶體地址,但是self.y+=1卻只是改變了self.y的指向,程式碼執行後self.y指向了另一塊記憶體地址而A.y指向的記憶體地址沒變,相當於建立了一個副本,所以在接下來生成b例項的時候b.y的值還是0,因為類變數y指向記憶體空間的值並沒有改變。