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