python進階(六)~~~元類和內存管理
一、元類
python2中所有類是基于instance創建的,稱為舊式類;注明繼承object時,稱為新式類;
python3中默認均為基于object,無區別;
object為所有類的基類,所有的類的繼承頂層父類都是object;
type為所有類的元類,print(type(類名)),輸出type,所有類的類型都是type。所有的類都是type創建出來的;
# 使用type動態地創建類 def func1(self): print(self.name) dict1={"name":"lala","age":18,"func":func1()} my=type("Myclass",bases=(object,),dict=dict1) # 第一個參數為:類名,第二個參數為繼承的類,第三個參數為類的屬性和方法 print(my) # 返回類名Myclass m1=my() print(type(m1)) # 返回類名Myclass m1.func()
二、內存管理
1對象引用
變量:通過變量指針指向具體對象的內存空間,取對象的值;
對象:類型已知,每個對象都包含頭部信息(類型標識符和引用計數器);
a=10 a這個變量指向10這個對象,10這個對象中包含類型標識符:int和引用計數器:1
b=a b引用a,b實際引用的也是10這個對象,此時,10的引用計數器為:2
引用記數減少的情況:引用對象的別名被顯示地銷毀;變量別名被賦予其他值;對象從容器中被移除,或容器被銷毀;一個引用離開了它的作用域;
1.1內置函數 is 和 id
is:判斷是否為同一個對象
id:查看變量指向的內存地址
li1=[11,22] li1=li2 li3=[11,22] print(id(li1)) print(id(li2)) # 與li1一樣 print(id(li3)) # 與li1不一樣 li1.append(123) #修改li1后,li2的值隨之改變。內存地址不變化 a=1000 a=b c=1000 print(id(a),id(b),id(c)) # a 、b內存地址一樣,c不一樣 a=999 print(a,b,c) # 此時 返回 999 1000 1000,b的值不隨之變化 print(id(a),id(b),id(c)) # a的內存地址變化,b、c的內存地址不變
如上所示,列表為可變類型數據,可在同一內存地址中追加或刪除;int為不可變類型數據,變化后內存地址一定會變化;
可變數據類型有:list、dict、set; 不可變數據類型有:數值類型、字符串、元祖
2.小整數池和intern機制
小整數池:自動將 -5 ~ 256 之間的數據進行緩存,方便下次直接引用,不需去創建;
大整數池即intern機制:存儲 純字符組合字符串(字母、數字、下劃線),不限長度。不含非標準字符,例如問號、空格;該機制的優點是創建字符串對象時,會優先在大整數池里查找是否已經存在相同的對象,如果有,直接拿過來使用,避免頻繁地創建和銷毀內存,提高性能。
3.深淺拷貝
import copy li=[11,22] li2=li.copy() # 將li完完整整的拷貝一份到另一塊內存地址 li3=li print(id(li),id(li2),id(li3)) #li 和 li3一樣,li2不一樣。當li變化時,li2不會變化 # 淺拷貝:拷貝父對象,不會拷貝對象的內部的子對象。 a=[1,2] li=[11,22,a] li2=li.copy() # 拷貝[11,22,a],當a變化時,li和li2都會變化。 #深拷貝:完全拷貝了父對象及其子對象 a=[1,2] li=[11,22,a] li2=copy.deepcopy(li) # 深拷貝 a.remove(3) print(li2) # 當a變化時,li2不受影響
4.垃圾回收和GC模塊
垃圾回收機制:以引用計數機制為主,標記清除和分代收集兩種機制為輔的策略。
引用計數:每個對象創建后都會有一個引用計數,當記數為0時,垃圾回收機制自動將之銷毀,回收內存空間。缺點是:兩個變量循環引用時,永遠不會被銷毀,最終導致內存泄漏。
標記清除:彌補引用計數的缺點。

在上圖中,可以從程序變量直接訪問塊1,并且可以間接訪問塊2和3。程序無法訪問塊4和5。第一步將標記塊1,并記住塊2和3以供稍后處理。第二步將標記塊2,第三步將標記塊3,但不記得塊2,因為它已被標記。掃描階段將忽略塊1,2和3,因為它們已被標記,但會回收塊4和5。
標記清除算法作為Python的輔助垃圾收集技術,主要處理的是一些容器對象,比如list、dict、tuple等,因為對于字符串、數值對象是不可能造成循環引用問題。Python使用一個雙向鏈表將這些容器對象組織起來。不過,這種簡單粗暴的標記清除算法也有明顯的缺點:清除非活動的對象前它必須順序掃描整個堆內存,哪怕只剩下小部分活動對象也要掃描所有對象。
分代回收:分代回收是建立在標記清除技術基礎之上的,是一種以空間換時間的操作方式。
Python將內存根據對象的存活時間劃分為不同的集合,每個集合稱為一個代,Python將內存分為了3“代”,分別為年輕代(新創建的對象做為0代)、中年代(第1代)、老年代(第2代),他們對應的是3個鏈表,它們的垃圾收集頻率與對象的存活時間的增大而減小。新創建的對象都會分配在年輕代,年輕代鏈表的總數達到上限即閾值700,中老代一般總數達到10時,Python垃圾收集機制就會被觸發,把那些可以被回收的對象回收掉,而那些不會回收的對象就會被移到中年代去,依此類推,老年代中的對象是存活時間最久的對象,甚至是存活于整個系統的生命周期內。

三種情況觸發垃圾回收:
1、調用gc.collect()
2、GC達到閥值時,0代觸發將清理所有三代,1代觸發會清理1,2代,2代觸發后只會清理自己
3、程序退出時
使用 gc.get_threshold(): 查看分代回收的閾值,返回(700,10,10); gc.set_threshold(100,5,5): 可設置修改分代回收的閾值

浙公網安備 33010602011771號