Python入門之python可變對象與不可變對象

來源:互聯網
上載者:User

標籤:對象   table   -o   出現   ...   魔法方法   參考   可變參數   self   

本文分為如下幾個部分

  • 概念
  • 地址問題
  • 作為函數參數
  • 可變參數在類中使用
  • 函數預設參數
  • 類的實現上的差異

 

 

概念

可變對象與不可變對象的區別在於對象本身是否可變。

python內建的一些類型中

  • 可變對象:list dict set
  • 不可變對象:tuple string int float bool

舉一個例子

# 可變對象>>> a = [1, 2, 3]>>> a[1] = 4>>> a[1, 4, 3]# 不可變對象>>> b = (1, 2, 3)>>> b[1] = 4Traceback (most recent call last):  File "<stdin>", line 1, in <module>TypeError: ‘tuple‘ object does not support item assignment

上面例子很直觀地展現了,可變對象是可以直接被改變的,而不可變對象則不可以。

 

地址問題

下面我們來看一下可變對象的記憶體位址變化

>>> a = [1, 2, 3]>>> id(a)2139167175368>>> a[1] = 4>>> id(a)2139167175368

我們可以看到,可變對象變化後,地址是沒有改變的

如果兩個變數同時指向一個地址

1.可變對象
>>> a = [1, 2, 3]>>> id(a)2139167246856>>> b = a>>> id(b)2139167246856>>> a[1] = 4>>> a[1, 4, 3]>>> b[1, 4, 3]>>> id(a)2139167246856>>> id(b)2139167246856

我們可以看到,改變ab也跟著變,因為他們始終指向同一個地址

2.不可變對象
>>> a = (1, 2, 3)>>> id(a)2139167074776>>> b = a>>> a = (4, 5, 6)>>> a(4, 5, 6)>>> b(1, 2, 3)>>> id(a)2139167075928>>> id(b)2139167074776

我們可以看到,a改變後,它的地址也發生了變化,而b則維持原來的地址,原來地址中的內容也沒有發生變化。

 

作為函數參數 1.可變對象
>>> def myfunc(l):...     l.append(1)...     print(l)...>>> l = [1, 2, 3]>>> myfunc(l)[1, 2, 3, 1]>>> l[1, 2, 3, 1]

我們可以看到,可變對象作為參數傳入時,在函數中對其本身進行修改,是會影響到全域中的這個變數值的,因為函數直接對該地址的值進行了修改。

2.不可變對象
>>> def myfunc(a):...     a += 1...     print(a)...>>> a = 2>>> myfunc(a)3>>> a2

對於不可變對象來說,雖然函數中的a值變了,但是全域中的a值沒變,因為函數中的a值已經對應了另外一個地址,而全域中的a值指向的原來地址的值是沒有變的。

  3.總結

python中向函數傳遞參數只能是引用傳遞,表示把它的地址都傳進去了,這才會帶來上面的現象。

有的程式設計語言允許值傳遞,即只是把值傳進去,在裡面另外找一個地址來放,這樣就不會影響全域中的變數。

 

可變參數在類中使用

我們直接來看下面這個例子

class Myclass:    def __init__(self, a):        self.a = a    def printa(self):        print(self.a)

運行如下

>>> aa = [1,2]>>> my = Myclass(aa)>>> my.printa()[1, 2]>>> aa.append(3)>>> my.printa()[1, 2, 3]

我們可以看到,類中的變數和全域變數地址依然是共用的,無論在哪裡修改都會影響對方。

其實這個特性也不能說是一個弊端,利用這一點可以進行一些很方便的操作,比如兩個線程同時操作一個隊列,我們不用設定一個global隊列,只要將隊列這個可變對象傳入類之中,修改就會自動同步。

下面這個生產者消費者例子就是這樣

import timeimport threadingimport randomfrom queue import Queueclass Producer(threading.Thread):    def __init__(self, queue):        threading.Thread.__init__(self)        self.queue = queue    def run(self):        while True:            random_integer = random.randint(0, 100)            self.queue.put(random_integer)            print(‘add {}‘.format(random_integer))            time.sleep(random.random())class Consumer(threading.Thread):    def __init__(self, queue):        threading.Thread.__init__(self)        self.queue = queue    def run(self):        while True:            get_integer = self.queue.get()            print(‘lose {}‘.format(get_integer))            time.sleep(random.random())def main():    queue = Queue()    th1 = Producer(queue)    th2 = Consumer(queue)    th1.start()    th2.start()if __name__ == ‘__main__‘:    main()

queue傳入兩個類中,在兩個類中隨意更改,自動在兩個類間同步。

 

函數預設參數

函數預設參數一定要設定為不可變參數,否則會引發一些錯誤,我們來看下面一個例子

>>> def myfunc(l=[]):...     l.append(‘add‘)...     print(l)...>>> myfunc([1, 2, 3])[1, 2, 3, ‘add‘]>>> myfunc([‘a‘, ‘b‘])[‘a‘, ‘b‘, ‘add‘]

上面代碼是正常啟動並執行,我們來看下面這些

>>> myfunc()[‘add‘]>>> myfunc()[‘add‘, ‘add‘]>>> myfunc()[‘add‘, ‘add‘, ‘add‘]

按理說應該每次都是[‘add‘],但是現在出現了意想不到的錯誤。

這是因為l = []是在函數定義時就確定下來的了,所以之後每次調用這個函數,使用的l都是同一個,如果不指定這個參數的新值,就會出現上面這個問題。

上面這個l可以預設設定為None,這就是一個不可變對象。

 

類的實現上的差異

其實list tuple裡的這些元素都相當於類的屬性,修改他們相當於修改類的屬性。

正常定義一個類它的屬性是可以正常訪問和修改的,所以那些類的執行個體都是可變對象。

我們只要定義一個類,不允許它修改屬性,就可以建立一個不可變對象。

這就要使用python的魔法方法,主要有兩種方法

  • 設定__setattr__直接拋出異常,即只要想設定屬性值,就會拋出異常
  • 設定__slot__限制屬性的訪問,如果屬性都不能訪問了,那就肯定不能修改

更細節的實現可以參考stackoverflow上的回答

 

 

 

參考

Python入門之python可變對象與不可變對象

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.