1. 程式人生 > 程式設計 >python新手學習可變和不可變物件

python新手學習可變和不可變物件

python中有可變物件和不可變物件,可變物件:list,dict.不可變物件有:int,string,float,tuple.

python不可變物件

int,tuple

先來看一個例子

def int_test(): 
  i = 77
  j = 77
  print(id(77))         #140396579590760
  print('i id:' + str(id(i)))   #i id:140396579590760
  print('j id:' + str(id(j)))   #j id:140396579590760
  print i is j          #True
  j = j + 1
  print('new i id:' + str(id(i))) #new i id:140396579590760
  print('new j id:' + str(id(j))) #new j id:140396579590736
  print i is j          #False
 
if __name__ == '__main__':
  int_test()

有i和j倆個變數的值為77,通過列印77的ID和變數i,j在記憶體中的id我們得知它們都是指向同一塊記憶體。所以說i和j都是指向同一個物件的。然後我們修改j的值,讓j的值+1.按道理j修改之後應該i的值也發生改變的,因為它們都是指向的同一塊記憶體,但結果是並沒有。因為int型別是不可變型別,所有其實是j複製了一份到新的記憶體地址然後+1,然後j又指向了新的地址。所以j的記憶體id發生了變化。

記憶體分配情況如下:

11111

有i和j倆個變數的值為77,通過列印77的ID和變數i,j在記憶體中的id我們得知它們都是指向同一塊記憶體。所以說i和j都是指向同一個物件的。然後我們修改j的值,讓j的值+1.按道理j修改之後應該i的值也發生改變的,因為它們都是指向的同一塊記憶體,但結果是並沒有。因為int型別是不可變型別,所有其實是j複製了一份到新的記憶體地址然後+1,然後j又指向了新的地址。所以j的記憶體id發生了變化。

記憶體分配情況如下:

def dict_test():
  a = {}
  b = a
  print(id(a))
  a['a'] = 'hhhh'
  print('id a:' + str(id(a)))
  print('a:' + str(a))
  print('id b:' + str(id(b)))
  print('b:' + str(b))if __name__ == '__main__':
  dict_test()

執行結果如下:

140367329543360
id a:140367329543360
a:{'a': 'hhhh'}
id b:140367329543360

b:{'a': 'hhhh'}

可以看到a最早的記憶體地址id是140367329543360 然後把a賦值給b其實就是讓變數b的也指向a所指向的記憶體空間。然後我們發現當a發生變化後,b也跟著發生變化了,因為list是可變型別,所以並不會複製一份再改變,而是直接在a所指向的記憶體空間修改資料,而b也是指向該記憶體空間的,自然b也就跟著改變了。

記憶體變化如下:

22222

python函式的引數傳遞

由於python規定引數傳遞都是傳遞引用,也就是傳遞給函式的是原變數實際所指向的記憶體空間,修改的時候就會根據該引用的指向去修改該記憶體中的內容,所以按道理說我們在函式內改變了傳遞過來的引數的值的話,原來外部的變數也應該受到影響。

但是上面我們說到了python中有可變型別和不可變型別,這樣的話,當傳過來的是可變型別(list,dict)時,我們在函式內部修改就會影響函式外部的變數。而傳入的是不可變型別時在函式內部修改改變數並不會影響函式外部的變數,因為修改的時候會先複製一份再修改。下面通過程式碼證明一下:

def test(a_int,b_list):
  a_int = a_int + 1
  b_list.append('13')
  print('inner a_int:' + str(a_int))
  print('inner b_list:' + str(b_list))
if __name__ == '__main__':
  a_int = 5
  b_list = [10,11]
  test(a_int,b_list)
  print('outer a_int:' + str(a_int))
  print('outer b_list:' + str(b_list))

執行結果如下:

inner a_int:6
inner b_list:[10,11,'13']
outer a_int:5
outer b_list:[10,'13']

答案顯而易見啦,經過test()方法修改後,傳遞過來的int型別外部變數沒有發生改變,而list這種可變型別則因為test()方法的影響導致內容發生了改變。

總結:

在很多的其他語言中在傳遞引數的時候允許程式設計師選擇值傳遞還是引用傳遞(比如c語言加上*號傳遞指標就是引用傳遞,而直接傳遞變數名就是值傳遞),而python只允許使用引用傳遞,但是它加上了可變型別和不可變型別,讓我們感覺有點混亂了。聽說python只允許引用傳遞是為方便記憶體管理,因為python使用的記憶體回收機制是計數器回收,就是每塊記憶體上有一個計數器,表示當前有多少個物件指向該記憶體。每當一個變數不再使用時,就讓該計數器-1,有新物件指向該記憶體時就讓計數器+1,當計時器為0時,就可以收回這塊記憶體了。

知識點擴充套件:

Python可變物件與不可變物件原理解析

原理

可變物件:list dict set

不可變物件:tuple string int float bool

1. python不允許程式設計師選擇採用傳值還是傳引用。Python引數傳遞採用的肯定是“傳物件引用”的方式。實際上,這種方式相當於傳值和傳引用的一種綜合。如果函式收到的是一個可變物件的引用,就能修改物件的原始值——相當於通過“傳引用”來傳遞物件。如果函式收到的是一個不可變物件的引用,就不能直接修改原始物件——相當於通過“傳值'來傳遞物件。

2. 當人們複製可變物件時,就複製了可變物件的引用,如果改變引用的值,則修改了原始的引數。

3. 為了簡化記憶體管理,Python通過引用計數機制實現自動垃圾回收功能,Python中的每個物件都有一個引用計數,用來計數該物件在不同場所分別被引用了多少次。每當引用一次Python物件,相應的引用計數就增1,每當消毀一次Python物件,則相應的引用就減1,只有當引用計數為零時,才真正從記憶體中刪除Python物件。

到此這篇關於python新手學習可變和不可變物件的文章就介紹到這了,更多相關python可變物件和不可變物件內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!