python 引用和對象理解

出處:http://www.cnblogs.com/ShaunChen/p/5656971.html

今天瀏覽博客的時候看到這麼一句話: python中變量名和對象是分離的;最開始的時候是看到這句話的時候沒有反應過來。決定具體搞清楚一下python中變量與對象之間的細節。(其實我感覺應該說 引用和對象分離 更爲貼切)

  從最開始的變量開始思考:

   在python中,如果要使用一個變量,不需要提前進行聲明,只需要在用的時候,給這個變量賦值即可 (這個和C語言等靜態類型語言不同,和python爲動態類型有關)。

   舉第一個栗子:

    a = 1

   這是一個簡單的賦值語句,整數 1 爲一個對象,a 是一個引用,利用賦值語句,引用a指向了對象1;這邊形象比喻一下:這個過程就相當於“放風箏”,變量a就是你手裏面的“線”,python就跟那根“線”一樣,通過引用來接觸和拴住天空中的風箏——對象。

   你可以通過python的內置函數 id() 來查看對象的身份(identity),這個所謂的身份其實就是 對象 的內存地址:

    注:

     python一切皆對象的理念,所以函數也是一個對象,因此可以使用 id() 函數的__doc__方法來查看這個函數的具體描述:

>>> id.__doc__
    "id(object) -> integer\n\nReturn the identity of an object. This is guaranteed to be unique among\nsimultaneously existing objects.       (Hint: it's the object's memory address.)"

 

  

   第二個栗子:

    a = 2

    a = 'banana'

   利用上面第一個栗子用到的 id()函數:    

>>> a = 1
>>> id(a) 
24834392
>>> a = 'banana'
>>> id(a)
139990659655312

    第一個語句中, 2是儲存在內存中的一個整數對象,通過賦值 引用a 指向了 對象 1

    第二個語句中,內存中建立了一個字符串對象‘banana’,通過賦值 將 引用a 指向了 ‘banana’,同時,對象1不在有引用指向它,它會被python的內存處理機制給當我垃圾回收,釋放內存。

 

   第三個栗子:

    a = 3

    b = 3

   通過函數查看 變量a 和 變量b的引用情況: 

>>> a = 3
>>> b = 3
>>> id(a)
10289448
>>> id(b)
10289448

  在這裏可以看到  這倆個引用 指向了同一個 對象,這是爲什麼呢? 這個跟python的內存機制有關係,因爲對於語言來說,頻繁的進行對象的銷燬和建立,特別浪費性能。所以在Python中,整數和短小的字符,Python都會緩存這些對象,以便重複使用。

 

   第四個栗子:

    1.  a = 4

    2.  b = a(這裏就是讓引用b指向引用a指向的那個對象)

    3.  a = a + 2

   通過函數查看引用情況:

    當執行到第2步的時候,查看一下 a 和 b 的引用:      

>>> a = 4
>>> b = a
>>> id(a)
36151568
>>> id(b)
36151568

    可以看到 a 和 b 都指向了 整數對象 4

    接下來指向第3步:

>>> a = a+2
>>> id(a)
36151520
>>> id(b)
36151568

    可以看到 a 的引用改變了,但是 b 的引用未發生改變;a,b指向不同的對象; 第3句對 a 進行了重新賦值,讓它指向了新的 對象6;即使是多個引用指向同一個對象,如果一個引用值發生變化,那麼實際上是讓這個引用指向一個新的引用,並不影響其他的引用的指向。從效果上看,就是各個引用各自獨立,互不影響。

 

   第五個栗子(這個栗子會涉及到 python中的 可變數據類型 和 不可變數據類型):

   開始這個栗子之前,請記得注意到 第四個栗子的不同之處。

     1.   L1 = [1, 2, 3]

     2.   L2 = L1

     3.   L1[0] = 10

   通過函數查看引用情況:

     當執行第1步 和 第二步 的時候,查看一下 L1 和 L2 的引用情況:

>>> L1 = [1,2,3]
>>> L2 = L1
>>> id(L1)
139643051219496
>>> id(L2)
139643051219496

     此時 L1 和 L2 的引用相同,都是指向 [1,2,3]這個列表對象。

     接下來,繼續執行第3步:

>>> L1[0] = 10
>>> id(L1)
139643051219496
>>> id(L2)
139643051219496
>>> L2
[10, 2, 3]

     同樣的跟第四個栗子那樣,修改了其中一個對象的值,但是可以發現 結果 並不與 第四個栗子那樣, 在本次實驗中,L1 和 L2 的引用沒有發生任何變化,但是 列表對象[1,2,3] 的值 變成了 [10,2,3](列表對象改變了)

     在該情況下,我們不再對L1這一引用賦值,而是對L1所指向的表的元素賦值。結果是,L2也同時發生變化。

     原因何在呢?因爲L1,L2的指向沒有發生變化,依然指向那個表。表實際上是包含了多個引用的對象(每個引用是一個元素,比如L1[0],L1[1]..., 每個引用指向一個對象,比如1,2,3), 。而L1[0] = 10這一賦值操作,並不是改變L1的指向,而是對L1[0], 也就是表對象的一部份(一個元素),進行操作,所以所有指向該對象的引用都受到影響。

(與之形成對比的是,我們之前的賦值操作都沒有對對象自身發生作用,只是改變引用指向。)

     列表可以通過引用其元素,改變對象自身(in-place change)。這種對象類型,稱爲可變數據對象(mutable object),詞典也是這樣的數據類型。

     而像之前的數字和字符串,不能改變對象本身,只能改變引用的指向,稱爲不可變數據對象(immutable object)。

     我們之前學的元組(tuple),儘管可以調用引用元素,但不可以賦值,因此不能改變對象自身,所以也算是immutable object.

        

    is關鍵字:

    當然,我們也可以要想知道是否指向同一個對象,我們可以使用 python的 is 關鍵詞,is用於判斷兩個引用所指的對象是否相同。

    就像上述第四個栗子 當進行到 第1步 和 第2步 的時候:

>>> a = 4  ……id(a) = 36151568
>>> b =a   ……id(b) = 36151568
>>> a is b  
True

    當進行到第3步的時候:

>>> a = a + 2  ……id(a) = 36151520
>>> a is b  ……id(b) = 36151568
False

              

    突然想到,對於python 的 深拷貝 和 淺拷貝 的理解,也是可以根據這個進行驗證,可以通過第五個栗子進行輔助理解。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章