09-Python面向物件-物件的生命週期,以及週期方法
阿新 • • 發佈:2018-12-19
學習地址:
撩課-Python大資料+人工智慧1
撩課-Python大資料+人工智慧2
撩課-Python大資料+人工智慧3
撩課-Python大資料+人工智慧4
撩課-Python大資料+人工智慧5
撩課-Python大資料+人工智慧6
撩課-Python-GUI程式設計-PyQt5
1.概念
生命週期
指的是一個物件, 從誕生到消亡的過程
當一個物件被建立時, 會在記憶體中分配相應的記憶體空間進行儲存
當這個物件不再使用, 為了節約記憶體, 就會把這個物件釋放
2.涉及問題
如何監聽一個物件的生命過程? Python是如何掌控一個物件的生命?
3.監聽物件生命週期
__new__方法
當我們建立一個物件是, 用於給這個物件分配記憶體的方法
通過攔截這個方法, 可以修改物件的建立過程
比如:單例設計模式
__init__方法
每個物件例項化的時候,都會自動執行這個方法
可以在這個方法裡面,初始化一些例項屬性
__del__方法
當物件被釋放的時候呼叫這個方法
可用於在這個方法中清理資源
4.記憶體管理機制
儲存方面
1. 在Python中萬物皆物件 不存在基本資料型別 0, 1.2, True, False, "abc" 這些全都是物件 2. 所有物件, 都會在記憶體中開闢一塊空間進行儲存 會根據不同的型別以及內容, 開闢不同的空間大小進行儲存 返回該空間的地址給外界接收(稱為"引用"), 用於後續對這個物件的操作 可通過id()函式獲取記憶體地址(10進位制) 通過hex()函式可以檢視對應的16進位制地址 3. 對於整數和短小的字元, Python會進行快取; 不會建立多個相同物件 此時, 被多次賦值, 只會有多份引用 4. 容器物件, 儲存的其他物件, 僅僅是其他物件的引用, 並不是其他物件本身 比如字典, 列表, 元組這些"容器物件" 全域性變數是由一個大字典進行引用 global()檢視
垃圾回收方面
1).引用計數器
概念
一個物件, 會記錄著自身被引用的個數
每增加一個引用, 這個物件的引用計數會自動+1
每減少一個引用, 這個物件的引用計數會自動-1
舉例
引用計數+1場景 物件被建立 p1 = Person() 物件被引用 p2 = p1 物件被作為引數,傳入到一個函式中 log(p1) 這裡注意會+2, 因為內部有兩個屬性引用著這個引數 物件作為一個元素,儲存在容器中 l = [p1] 引用計數-1場景 物件的別名被顯式銷燬 del p1 物件的別名被賦予新的物件 p1 = 123 一個物件離開它的作用域 一個函式執行完畢時 內部的區域性變數關聯的物件, 它的引用計數就會-1 物件所在的容器被銷燬,或從容器中刪除物件
檢視引用計數
import sys
sys.getrefcount(物件)
注意會大一
垃圾回收
主要作用
從經歷過"引用計數器機制"仍未被釋放的物件中, 找到"迴圈引用", 幹掉相關物件底層機制(瞭解&難)
怎樣找到"迴圈引用"?
1. 收集所有的"容器物件", 通過一個雙向連結串列進行引用
容器物件
可以引用其他物件的物件
列表
元組
字典
自定義類物件
...
非容器物件
不能引用其他物件的物件
數值
字串
布林
...
注意: 針對於這些物件的記憶體, 有其他的管理機制
2. 針對於每一個"容器物件", 通過一個變數gc_refs來記錄當前對應的引用計數
3. 對於每個"容器物件",找到它引用的"容器物件", 並將這個"容器物件"的引用計數 -1
4. 經過步驟3之後, 如果一個"容器物件"的引用計數為0, 就代表這玩意可以被回收了, 肯定是"迴圈引用"導致它活到現在的
如何提升查詢"迴圈引用"的效能?
如果程式當中建立了很多個物件, 而針對於每一個物件都要參與"檢測"過程; 則會非常的耗費效能
所以, 基於這個問題, 產生了一種假設:
越命大的物件, 越長壽
假設一個物件10次檢測都沒給它幹掉, 那認定這個物件一定很長壽, 就減少這貨的"檢測頻率"
基於這種假設, 設計了一套機制
分代回收
機制
1. 預設一個物件被創建出來後, 屬於 0 代
2. 如果經歷過這一代"垃圾回收"後, 依然存活, 則劃分到下一代
3. "垃圾回收"的週期順序為
0代"垃圾回收"一定次數, 會觸發 0代和1代回收
1代"垃圾回收"一定次數, 會觸發0代, 1代和2代回收
檢視和設定相關引數
import gc
print(gc.get_threshold())
gc.set_threshold(700, 10, 5)
垃圾回收器當中, 新增的物件個數-消亡的物件個數 , 達到一定的閾值時, 才會觸發, 垃圾檢測
垃圾回收時機(掌握&簡單)
1. 自動回收
觸發條件
開啟垃圾回收機制
gc.enable()
開啟垃圾回收機制(預設開啟)
gc.disable()
關閉垃圾回收機制
gc.isenabled()
判定是否開啟
並且
達到了垃圾回收的閾值
垃圾回收器中, 新增的物件個數和釋放的物件個數之差到達某個閾值
涉及方法
gc.get_threshold()
獲取自動回收閾值
gc.set_threshold()
設定自動回收閾值
2. 手動回收
觸發條件
gc.collect()
執行一次垃圾回收(開關狀態無效)
特殊場景
場景條件
Python2.x版本下, 迴圈引用, 並且有一個物件都實現了__del__方法
概念
兩個物件互相引用物件, 誰的引用計數都是1
兩個物件都
導致結果
無法釋放, 進行記憶體回收
底層原因
無法判別先釋放哪個物件, 呼叫哪一個del方法
解決思路
手動解除迴圈引用
方式1
預防
儘可能避免迴圈引用產生
實現
一方使用弱引用代替
方式2
治療
在迴圈引用的產生的前提下
實現
當刪除一個引用, 確定以後不再使用時, 手動清空對其他容器物件的引用
測量物件的引用個數
輔助工具
objgraph
http://mg.pov.lt/objgraph/
xdot
graphviz