python 的内存回收,及深浅Copy详解
數字(num)、字符串(str)、元組(tuple)、布爾值(bool<True,False>)
接下來我們講完后你就懂了為什么它們是不可變對象了。
都知道python中一切都是對象,而變量就是這些對象的引用,什么意思呢
綜合表述:
變量是一個系統表的元素,擁有指向對象的連接的空間
對象是被分配的一塊內存,存儲其所代表的值
引用是自動形成的從變量到對象的指針
特別注意: 類型屬于對象,不是變量
>>> c = 17 #1 數字17就是一個對象,實實在在存在計算機內存中 >>> d = c #2 c 和 d 都是對象17的一個引用,c指向17,d也是 >>> id(c) #3 1462698960 >>> id(d) #4 1462698960在#1 處我們定義了各一個變量c,c指向了17(把17賦值給c),對象17的一個引用c
然后在#2處,又定義了一個變量d ,把c賦值給了d,接著#3、#4查看了c、d的 id 相同,
發現是同一個對象(17),對象17的引用+1
引用:
對象17的引用現在有兩個了
變量:
在內部,變量事實上是到對象內存空間的一個指針
1.2.1 python本身是一門動態語言
與c/c++ /java不同,不需要事先定義變量開辟內存空間,然后給變量賦值,存儲到變量的內存空間中。使用結束,當然也不需要你去手動調用析構函數釋放內存了。
python會預先申請一部分內存空間,在運行時定義了變量-對象,根據對象確認它的type,將對象放到申請的內存中,python每過一段時間就來檢查一次,當有對象的引用為0時,就回收這塊內存,返還回先申請的內存空間,而不是計算機。這樣避免了內存碎片過多問題。
1.2.2 怎么減少對象的引用
可以看到#1、#2處c、d都還是對象17的引用,當#3處把變量c 指向新對象字符串"yue" 時,#4處發現變量c指向的對象id變了,的確不是17了,所以對象17的引用 -1 如下圖
注意:這兒改變了c的引用,可是#5處d卻沒有跟著c變,還是對象17
同理當你再把d指向其他對象時,對象17的引用就減為零,當Python來檢查時,就會回收這塊內存了
2.刪除變量(引用)
>>> del d >>> d Traceback (most recent call last):File "<stdin>", line 1, in <modul NameError: name 'd' is not defined不啰嗦,這樣對象17就徹底被刪除了,上圖時對象17只剩下一個變量引用d。
同理對于函數,定義函數時,函數名就是一個引用,當其他地方調用函數時,引用+1,調用結束 -1 。在函數的命名空間中可以查到這些,詳情看我這篇文章
python的內存回收就到這兒:總結:回收機制為判斷對象 引用是否為0,如果為零就回收內存到自己申請的內存空間,不是計算機硬盤。
1.3 再談不可變類型通過上面的式子和圖理解我們也知道了,當定義變量為數字、字符串、tuple、布爾值時,這些變量所對應的對象在內存空間的值是不可改變了,你重新賦值,也只是把變量引用指向了另一個對象,id變了,本身那個對象是不可變的。
>>> a = (1, 'one') >>> id(a) 612494666568 #1 >>> a[0] = 2 Traceback (most recent call last):File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment >>> a[0] 1 >>> a = (2, 'two') >>> id(a) 612494666824 #2#--------------------------------------------- >>> a = 'findxgo' #3 >>> id(a) 612496082848 >>> a.replace('x','--X--') #4 'find--X--go' >>> id(a) #5 612496082848 >>> a = a.replace('x', '-X-') #6 >>> id(a) 612496086704在#3出定義了字符串a,#4處替換x,得到一新字符串,但是原字符串還是#5id沒變,當#6把替換的字符串賦值給變量a,a的引用指向了替換后新字符串
二、python中的深淺Copy 2.1 共享引用如圖:指兩個或多個變量指向同一個內存空間
如果刪掉c后, 不會影響d
拷貝概念的引入就是針對:可變對象的共享引用潛在的副作用而提出的。
2.2 可變對象2.2.1 指python中,存儲在內存可以被修改的對象:列表、字典等
上面說的數字、字符串、元組等不可變類型,在你復制時也就是增加了一個引用,無法去改變內存的值。對對象的其中一個引用變量操作不會影響其他引用。
但是對于列表、字典:
可以看到#1 上面定義一個列表,賦值給L2后,L2、list_1對應完全一樣的值(列表)事實上,他兩的確對應著一塊內存,你可以自己去查id,是那塊內存(列表)的兩個引用
當你去在list_1,或者L2進行操作時,改變了對應內存的值,所以#2下面兩個值都變了。python中同一塊內存(對象)的不同引用改變對象所以引用都會被影響。
同理字典:通過自己哈希表將key計算后得到的內存地址就是存放value的地方,當你用如上同樣的方式改變哪兒的值,所有引用都會被影響。
上述的情況如果想避免,有兩種方式,原理都一樣:copy一份放到另一個內存,變成同樣(value)的兩個對象,當你修改其中一個時,另一個不會影響。
1、切片復制:完全切片
如上:#1處完全切片也可以L2 = list_1[0: -1]是一樣的,#2,#3處可以看見,id不同,就不是同一個對象,只是里面的value相同而已
同理copy模塊的copy方法,這是淺拷貝。
2.改變源頂級對象,深淺拷貝不會變
>>> dict_1['copy'] = 'n_copy' >>> dict_1;D2;D3 {'copy': 'n_copy', 'deepcopy': ['deep', '第二層', ' 深拷貝']} {'copy': '淺拷貝', 'deepcopy': ['deep', '第二層', ' 深拷貝']} {'copy': '淺拷貝', 'deepcopy': ['deep', '第二層', ' 深拷貝']}3.改變源嵌套對象,淺拷貝變了,深拷貝不變
>>> dict_1['deepcopy'][1] = '嵌套層' >>> dict_1;D2;D3 {'copy': 'n_copy', 'deepcopy': ['deep', '嵌套層', ' 深拷貝']} {'copy': '淺拷貝', 'deepcopy': ['deep', '嵌套層', ' 深拷貝']} {'copy': '淺拷貝', 'deepcopy': ['deep', '第二層', ' 深拷貝']}這兒的淺拷貝,只拷貝了父級對象,在'deepcopy'對應的哪兒就是只拷貝了內存地址,而深拷貝還要去內存地址拷貝內容回來賦值
原理看到這兒,差不多也懂了,就不羅嗦了!
- 深淺拷貝都是對源對象的復制,占用不同的內存空間
- 如果源對象只有一級目錄的話,源做任何改動,不影響深淺拷貝對象
- 如果源對象不止一級目錄的話,源做任何改動,都要影響淺拷貝,但不影響深拷貝
- 序列對象的切片其實是淺拷貝,即只拷貝頂級的對象
一個有意思的練習題
import copy a = [1,2,3,[4,5],6] b=a c=copy.copy(a) d=copy.deepcopy(a) b.append(10) c[3].append(11) d[3].append(12)a,b,c,d分別為什么?
答案我放評論
轉載于:https://www.cnblogs.com/shiqi17/p/9417663.html
總結
以上是生活随笔為你收集整理的python 的内存回收,及深浅Copy详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 皮小秀乌迪尔符文天赋(上单乌迪尔天赋符文
- 下一篇: 联想电脑保修期(联想电脑保修期一般多长时