簡體   English   中英

__del__的用例

[英]Use cases for __del__

在python 3中編寫自定義__del__方法或依賴stdlib 1中的一個用例是什么? 也就是說,在什么情況下它是相當安全的,並且可以做一些沒有它的事情很難做到的事情?

由於很多好的理由( 1 2 3 4 5 6 ),通常的建議是避免使用__del__而是使用上下文管理器或手動執行清理:

  1. __del__不能保證,如果對象是在intrepreter出口2活活被調用。
  2. 在點1期望對象可以被破壞時,引用計數實際上可以是非零的(例如,引用可以通過由調用函數保持的回溯幀而存活)。 這使得破壞時間遠比gc意味着的不可預測性更不確定。
  3. 垃圾收集器如果包含多個帶有__del__對象,則無法擺脫循環
  4. 必須仔細編寫__del__的代碼:
    • __init__設置的對象屬性可能不存在,因為__init__可能引發了異常;
    • 異常被忽略(僅打印到stderr );
    • 全局變量可能不再可用。

更新:

PEP 442__del__的行為做出了重大改進。 雖然我的第1-4點仍然有效?


更新2:

一些頂級的Python庫涵蓋使用的__del__在PEP后442蟒蛇(即蟒蛇3.4+)。 我想我的觀點3在PEP 442之后不再有效,其他點被認為是對象終結的不可避免的復雜性。


1我從擴大只是寫一個自定義的問題__del__方法包括依靠__del__從STDLIB。

2似乎__del__一直呼吁在較新版本的CPython的解釋退出(沒有任何人有一個反例?)。 不過,不要緊為目的__del__的usablity:該文檔明確不提供有關此問題的保障 ,因此不能依賴它(它可能在未來的版本中改變,它可能會在非CPython的解釋不同)。

上下文管理器(和try / finally塊)比__del__更具限制性。 通常,它們要求您構造代碼,使得您需要釋放的資源的生命周期不超出調用堆棧中某個級別的單個函數調用,而不是將其綁定到生命周期可以在不可預測的時間和地點銷毀的類實例。 將資源的生命周期限制在一個范圍內通常是一件好事,但有時會出現這種模式難以適應的邊緣情況。

這里我使用的唯一情況__del__ (從調試不談,CF @ MSeifert的答案)是由外部庫釋放的Python以外分配的內存。 由於我正在包裝的庫的設計,很難避免有大量的對象持有指向堆分配的內存的指針。 使用__del__方法釋放指針是最簡單的清理方法,因為將每個實例的生命周期封裝在上下文管理器中是不切實際的。

一個用例是調試。 如果要跟蹤特定對象的生命周期,可以方便地編寫臨時 __del__方法。 它可以用來做一些日志記錄或只是print一些東西。 我已經使用了幾次,特別是當我對何時以及如何刪除實例感興趣時。 知道何時創建和丟棄大量臨時實例有時會很好。 但正如我所說,我只是用它來滿足我的好奇心或調試時。

另一個用例是子類化定義__del__方法的類。 有時您會發現要子類化的類,但內部要求您實際覆蓋__del__以控制實例清理的順序 這也是非常罕見的,因為你需要找到一個帶有__del__類,你需要將它子類化,你需要引入一些內部實際上需要在恰當的時間調用超類__del__ 我實際上曾經這樣做了一次,但我不記得它在哪里以及為什么它很重要(也許我當時甚至都不知道替代品,所以將其視為可能的用例)。

當你包裝一個外部對象(例如一個沒有被Python跟蹤的對象)的時候,即使有人“忘記”(我懷疑很多人只是故意省略它們) ,真的需要釋放它們才能使用您提供的上下文管理器。


然而,所有這些情況都是(或應該)非常非常罕見。 實際上它有點像元類:它們很有趣,理解概念真的很酷,因為你可以探索python的“有趣部分”。 但在實踐中:

如果你想知道你是否需要它們[metaclasses],你就不會(實際需要它們的人確切地知道他們需要它們,並且不需要解釋為什么)。

引用(可能)來自蒂姆彼得斯(我沒有找到原始參考)。

一個情況下我總是用__del__ ,是用於關閉aiohttp.ClientSession對象。

如果不這樣做,aiohttp將打印有關未關閉的客戶端會話的警告。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM