【轉載】python變量賦值(可變與不可變)

原文鏈接:http://www.cnblogs.com/evening/archive/2012/04/11/2442788.html

知識點:python中,萬物皆對象。

   python中不存在所謂的傳值調用,一切傳遞的都是對象的引用,也可以認爲是傳址。

python中,對象分爲可變(mutable)和不可變(immutable)兩種類型,元組(tuple)、數值型(number)、字符串(string)均爲不可變對象,而字典型(dictionary)和列表型(list)的對象是可變對象。

不可變類型特點:

  看下面的例子(例1)

>>>a = 1 #將名字a與內存中值爲1的內存綁定在一起
>>>a = 2 #將名字a與內存中值爲2的內存綁定在一起,而不是修改原來a綁定的內存中的值,這時,內存中值爲1的內存地址引用計數-1,當引用計數爲0時,內存地址被回收
>>>b = a #變量b執行與a綁定的內存
>>>b = 3 #創建一個內存值爲3的內存地址與變量名字b進行綁定。這是a還是指向值爲2的內存地址。
>>>a,b
>>>(2,3)

  這種機制的好處有哪些,弊端有哪些?

  看一個例子(例2)

>>>x = 1
>>>y = 1
>>>x = 1
>>> x is y
True
>>>y is z
True

  如上所示,因爲整數爲不可變,x,y,z在內存中均指向一個值爲1的內存地址,也就是說,x,y,z均指向的是同一個地址,值得注意的是,整形來說,目前僅支持(-1,100)。

  總結一下,不可變對象的優缺點。

    優點是,這樣可以減少重複的值對內存空間的佔用?。

    缺點呢,如例1所示,我要修改這個變量綁定的值,如果內存中沒用存在該值的內存塊,那麼必須重新開闢一塊內存,把新地址與變量名綁定。而不是修改變量原來指向的內存塊的值,這回給執行效率帶來一定的降低。

  下面看一個可變對象的例子(例3)

>>>a = [1]
>>>b = a #由於列別是可變對象類型,所以傳遞的時候,與變量名d綁定的內存地址與a綁定的內存地址是同一地址,內存裏的值是[1]

>>>b[0] = 2
>>>a
[2]

  如上所示:變量名a和b是綁定的同一內存地址,對任一個變量對應的值得改變,都會反映到另一個變量上。

  最後再看一個例子  

def mutable(b = []): #函數使用了缺省變量
    b.append(0)
    return b
>>>mutable()
[0]
>>>mutable()
[0,0]
>>>mutable()
[0,0,0]

  這裏連續三次以缺省值,運行函數3此,每次的結果都不一樣,按我們的想想,三次的結果,應該是一樣的,都爲[0],但是...

  那麼原因是什麼呢,前面說過,一切皆爲對象,函數mutable也爲一個對象,使用dir()查看函數的屬性:

  dir(mutable)

  ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

  mutable.__defaults__#函數對象的默認參數

  ([0,0,0],)

     上面我們三次運行了mutable這個函數,如果用mutable.__defaults__來查看函數對象的默認參數變化的話,就會發現問題了。

>>>mutable.__defaults__
([],)
>>>mutable()
[0]
>>>mutable.__defaults__
([0],)
>>>mutable()
[0,0]
>>>mutable.__defaults__
([0,0],)

  呵呵,終於明白了,原來,每運行一次,函數作爲一個對象的內在屬性的值發生了變化。導致每次運行的結果不一致。

  在編程過程中,如果不注意此類問題,很容易造成不可預料的錯誤。

  對於類來說也是如此

class b:
    x = []
    def set(self):
        self.x.append(1)
    def get(self):
        return self.x

>>>for i in range(3):
..........a = b()
..........b.__dict__
..........a.set()
..........a.get()

dict_proxy({'__module__': '__main__', 'set': <function set at 0x021ED3D8>, 'get': <function get at 0x021ED420>, '__dict__': <attribute '__dict__' of 'b' objects>, 'x': [], '__weakref__': <attribute '__weakref__' of 'b' objects>, '__doc__': None})
[1]
dict_proxy({'__module__': '__main__', 'set': <function set at 0x021ED3D8>, 'get': <function get at 0x021ED420>, '__dict__': <attribute '__dict__' of 'b' objects>, 'x': [1,], '__weakref__': <attribute '__weakref__' of 'b' objects>, '__doc__': None})
[1, 1]
dict_proxy({'__module__': '__main__', 'set': <function set at 0x021ED3D8>, 'get': <function get at 0x021ED420>, '__dict__': <attribute '__dict__' of 'b' objects>, 'x': [1, 1], '__weakref__': <attribute '__weakref__' of 'b' objects>, '__doc__': None})
[1, 1, 1]

仔細觀察,類對象內部屬性__dict__中'x'對應的值,在每創建一個對象時都發生了變化。也就是說,在每次創建類對象時,變量x引用內存的初始值是不同的,這終要歸因於列表(list)的可變性導致的。每次創建對象時,因爲列表的可變性,函數對象b的__dict__屬性中,x鍵對應的值,被改變,而不是重新創建,所以出現了上面的結果。

綜上:初學者如果不充分理解python的變量和類型和參數傳遞方式,或者是一切解釋對象的原理,會很容易產生上面的錯誤.

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