前面已經熟悉了python記憶體**機制:引用計數,分代**。
但是,仍有乙個問題:無法**迴圈引用物件。
只有容器物件才會形成迴圈引用,比如list、class、deque、dict、set等都屬於容器型別。
import gc測試結果:class a(object):
def __init__(self):
self.data = [x for x in range(100000)]
self.child = none
def __del__(self):
pass
def cycle_ref():
a1 = a()
a2 = a()
a1.child = a2
a2.child = a1
if __name__ == '__main__':
import time
while true:
time.sleep(0.5)
cycle_ref()
理論上a1,a2都是區域性變數,應該被**,但實際是如果使用迴圈引用,程序占用記憶體會不停飆公升;
注釋掉迴圈引用的兩句,程序占用記憶體保持在11.5m。
結果表明,迴圈引用會導致記憶體洩漏。
網上的文件基本上表示gc無法**迴圈引用的物件。
繼續測試
import gc測試結果(python 3.7.7, win8, idle):class a(object):
def __init__(self):
self.data = [x for x in range(100000)]
self.child = none
def __del__(self):
pass
def cycle_ref():
a1 = a()
a2 = a()
a1.child = a2
a2.child = a1
if __name__ == '__main__':
import time
while true:
time.sleep(0.5)
cycle_ref()
gc.collect()
print(gc.garbage)
手動gc.collect()可以**迴圈引用物件,記憶體占用保持在12m左右;
del方法並不會影響**。部分文件聲稱類自定義del方法會導致gc無法**迴圈引用物件,實測結果並不支援這一點;
gc.garbage輸出仍保持空。
解決方法有兩種:手動/自動。
手動很簡單,在不使用時解除引用。
def cycle_ref():當然,這比較考驗開發的功底,也容易出錯,weakref就是自動化的解決方案。a1 = a()
a2 = a()
a1.child = a2
a2.child = a1
# 解除迴圈引用,避免記憶體洩露
a1.child = none
a2.child = none
python標準庫提供了weakref模組,弱引用不會在引用計數中計數,其主要目的是解決迴圈引用。並非所有的物件都支援weakref,例如list和dict就不支援。下面是weakref比較常用的方法:
class weakref.ref(object[, callback]) :建立乙個弱引用物件,object是被引用的物件,callback是**函式(當被引用物件被刪除時,呼叫該**函式);
weakref.proxy(object[, callback]):建立乙個用弱引用實現的**物件,引數同上;
weakref.getweakrefcount(object) :獲取物件object關聯的弱引用物件數;
weakref.getweakrefs(object):獲取object關聯的弱引用物件列表;
class weakref.weakkeydictionary([dict]):建立key為弱引用物件的字典;
class weakref.weakvaluedictionary([dict]):建立value為弱引用物件的字典;
class weakref.weakset([elements]):建立成員為弱引用物件的集合物件。
使用r_obj = weakref.ref(obj)建立的物件引用時需要使用r_obj()形式呼叫,所以常用做法是使用r_obj = weakref.proxy(obj);這樣呼叫時可以直接使用r_obj。
對上面的案例**改造一下便可解決記憶體洩漏問題:
import gc下面的**中gc.garbage總是為空,與文件所描述的情形不符。import weakref
class a(object):
def __init__(self):
self.data = [x for x in range(100000)]
self.child = none
def __del__(self):
pass
def cycle_ref():
a1 = a()
a2 = a()
a1.child = weakref.proxy(a2)
a2.child = weakref.proxy(a1)
if __name__ == '__main__':
import time
while true:
time.sleep(0.5)
cycle_ref()
#gc.collect()
print(gc.garbage)
可以觀察到刪除迴圈引用的兩個物件時不一定會立即**,需要手動呼叫gc.collect(0、1、2)才能**。
但gc.garbage始終為空。
class a():在3.7.7中gc可以**迴圈引用物件;def __del__(self):
print('del a')
class b():
def __del__(self):
print('del b')
a = a()
b = b()
a._n = b
b._n = a
import gc
class node(object):
def __init__(self, data):
self.data = data
self.parent = none
self.children =
def add_child(self, child):
child.parent = self
def __del__(self):
print('__del__')
n = node(0)
del n
# __del__
n1 = node(1)
n2 = node(2)
n1.add_child(n2)
del n1 # no output
n2.parent
del n2
但既然weakref存在,並且很多模組也在使用它,就目前的情況而言,在開發時使用weakref而不是依賴於gc是一件惠而不費的事情 。
for迴圈 while迴圈
迴圈結構 當重複執行相同的 或者是相似的 時。迴圈三要素 1 迴圈變數的宣告 用於控制迴圈次數的迴圈因子 2 迴圈條件 用於判斷是否執行相同或相似內容 迴圈體 的條件 3 迴圈變數的改變方向 向著迴圈結束的方向改變。1 for迴圈 語法 for 變數的宣告和初始化 迴圈條件 變數的改變方向 執行邏輯...
python while迴圈 for迴圈
1變數的初始化 while 條件2 條件滿足時候 執行該 條件滿足時候 執行該 3變數的更新 1 while 迴圈輸出1 100所有的數 while 迴圈輸出20次我愛你 迴圈輸出1 100累加和 1 100之間所有數的和 1變數的初始化 i 0 sum 0 儲存和 判斷條件 while i 100...
python while 迴圈 if 迴圈
python 程式設計中 while 語句用於迴圈執行程式,即在某條件下,迴圈執行某段程式,以處理需要重複處理的相同任務。其基本形式為 執行語句可以是單個語句或語句塊。判斷條件可以是任何表示式,任何非零 或非空 null 的值均為true。當判斷條件假false時,迴圈結束。執行流程圖如下 prin...