python中有可變物件和不可變物件,可變物件:list,dict.不可變物件有:int,string,float,tuple.
python不可變物件
int,string,float,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發生了變化。
記憶體分配情況如下:
有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:id b:140367329543360
b:可以看到a最早的記憶體位址id是140367329543360 然後把a賦值給b其實就是讓變數b的也指向a所指向的記憶體空間。然後我們發現當a發生變化後,b也跟著發生變化了,因為list是可變型別,所以並不會複製乙份再改變,而是直接在a所指向的記憶體空間修改資料,而b也是指向該記憶體空間的,自然b也就跟著改變了。
記憶體變化如下:
python函式的引數傳遞
由於python規定引數傳遞都是傳遞引用,也就是傳遞給函式的是原變數實際所指向的記憶體空間,修改的時候就會根據該引用的指向去修改該記憶體中的內容,所以按道理說我們在函式內改變了傳遞過來的參nlhao數的值的話,原來外部的變數也應該受到影響。
但是上面我們說到了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_nlhaoint:5
outer b_程式設計客棧list:[10, 11, '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學習 可變型別和不可變型別
不可變資料型別在第一次宣告賦值宣告的時候,會在記憶體中開闢一塊空間,用來存放這個變數被賦的值,而這個變數實際上儲存的,並不是被賦予的這個值,而是存放這個值所在空間的記憶體位址,通過這個位址,變數就可以在記憶體中取出資料了.所謂不可變就是說,我們不能改變這個資料在記憶體中的值,所以當我們改變這個變數的...
可變和不可變
可變和不可變 不可變 物件所指向的記憶體中的值是不可以改變 不可變型別 int ser float 元組tuple num 10s1 abc print id s1 s1 abcd print id s1 t1 2 5,6 print id t1 t1 2 5 print id t1 可變的 物件所...
python不可變型別和可變型別
python變數可以分為兩種型別 不可變型別 數字 字串 元組 不可變集合 可變型別 列表 字典 可變集合 python所宣告的變數都以物件的形式存在,存在於機器的固定記憶體之中。可以理解為變數名為物件的指標 如宣告a 3,則a指向儲存3的空間,python通過使用計數器的方式來判斷空間的引用情況,...