內存泄漏太可怕。
Python 可變對象 & 不可變對象
在Python中,對象分為兩種:可變對象和不可變對象。
- 不可變對象包括int,float,long,str,tuple等;
- 可變對象包括list,set,dict等。
需要注意的是:這里說的不可變指的是值的不可變。對于不可變類型的變量,如果要更改變量,則會創建一個新值,把變量綁定到新值上,而舊值如果沒有被引用就等待垃圾回收。另外,不可變的類型可以計算hash值,作為字典的key。可變類型數據對對象操作的時候,不需要再在其他地方申請內存,只需要在此對象后面連續申請(+/-)即可,也就是它的內存地址會保持不變,但區域會變長或者變短。
如下所示:
list = [1, 2, 3]
print(id(list))
arr = [4, 5]
list = list + arr
print(id(list))
list2 = [1, 2, 3]
print(id(list2))
list2 += [4, 5]
print(id(list2))
# 輸出結果:
# 4557530312
# 4557530696
# 4557530312
# 4557530312
- list = list + arr,list出現兩次,必須執行兩次,性能不好,合并必須新建對象list,然后復制兩個列表合并,這屬于拷貝;
- list += arr,list只出現一次,因為不生成新對象,因此性能好,只在內存塊末尾增加元素。當操作元素為list時,“+=”會自動調用 extend 方法進行合并運算,這屬于共享引用.
Python沒有賦值,只有引用
Python中的“=”傳遞的是引用(內存地址),應按照C語言中的指針理解。
Python淺拷貝copy.copy()
Python中,對于非容器類型(如數字、字符串、和其他原子類型的對象)沒有被拷貝一說。
使用copy.copy(),可以進行對象的淺拷貝,它復制了對象,但對于對象中的子對象,依然使用原始的引用,所以原始數據改變,子對象會改變。
該示例顯示了通過傳遞引用的方式創建obj1,此時obj1 is list
list = [1, 2, 3, ['a', 'b']]
obj1 = list
# 輸出list與obj1的地址
print('id of list =', id(list))
print('id of obj1 =', id(obj1))
# obj1原始的值
print('value of obj1 =', obj1)
# 修改list的值,看obj1中對應的值是否改變
list[1] = 999
print('value of obj1 =', obj1)
# 輸出結果:
# id of list = 4367528520
# id of obj1 = 4367528520
# value of obj1 = [1, 2, 3, ['a', 'b']]
# value of obj1 = [1, 999, 3, ['a', 'b']]
接下來通過淺拷貝創建obj2,可以看到此時list與obj2指向了不同的地址,修改list中的字符串、布爾類型、整數、浮點數、數字不會改變obj2中的值;但是此時obj2的子對象還是與list中的子列表共享一塊內存
obj2 = copy.copy(list)
# 輸出list與obj2的地址
print('id of list =', id(list))
print('id of obj2 =', id(obj2))
# 輸出list與obj2中每個元素的地址
print('id of list = ', [id(it) for it in list])
print('id of obj2 = ', [id(it) for it in obj2])
# 修改list[0]的值,看obj2中的值是否改變
list[0] = -1
print('value of list =', list)
print('value of obj2 =', obj2)
print('-' * 50)
# 修改list中的子列表,看obj2中的值是否改變
list[3].append('c')
print('value of obj2 =', obj2)
# 輸出list與obj2中每個元素的地址
print('id of list = ', [id(it) for it in list])
print('id of obj2 = ', [id(it) for it in obj2])
# 輸出結果:
# id of list = 4367528520
# id of obj2 = 4367558344
# id of list = [4345380112, 4346236624, 4345380176, 4367558536]
# id of obj2 = [4345380112, 4346236624, 4345380176, 4367558536]
# value of list = [-1, 999, 3, ['a', 'b']]
# value of obj2 = [1, 999, 3, ['a', 'b']]
# --------------------------------------------------
# value of obj2 = [1, 999, 3, ['a', 'b', 'c']]
# id of list = [4345380048, 4346236624, 4345380176, 4367558536]
# id of obj2 = [4345380112, 4346236624, 4345380176, 4367558536]
當我們使用下面的操作的時候,會產生淺拷貝的效果:
- 使用切片[:]操作
- 使用工廠函數(如list/dir/set)
- 使用copy模塊中的copy()函數
Python深拷貝copy.deepcopy()
復制一個容器對象,并且,以及它里面的所有元素(包含元素的子元素),可以使用copy.deepcopy()進行深拷貝
list = [1, 2, 3, ['a', 'b']]
obj3 = copy.deepcopy(list)
# 輸出list與obj3的地址
print('id of list =', id(list))
print('id of obj3 =', id(obj3))
# 輸出list與obj3中每個元素的地址
print('id of list = ', [id(it) for it in list])
print('id of obj2 = ', [id(it) for it in obj3])
# 修改list中的子列表,看obj2中的值是否改變
list[3].append('c')
# 修改list[0]的值,看obj2中的值是否改變
print('value of list =', list)
print('value of obj2 =', obj3)
# 輸出結果:
# id of list = 4370554760
# id of obj3 = 4370650248
# id of list = [4348476688, 4348476720, 4348476752, 4370650440]
# id of obj2 = [4348476688, 4348476720, 4348476752, 4370650120]
# value of list = [1, 2, 3, ['a', 'b', 'c']]
# value of obj2 = [1, 2, 3, ['a', 'b']]
關于Python內存管理機制請見:
Python源碼閱讀-內存管理機制(一)
Python源碼閱讀-內存管理機制(二)