《Python面向對象編程指南》——2.7 __del__()方法

本節書摘來自異步社區《Python面向對象編程指南》一書中的第2章,第2.7節,作者[美]Steven F. Lott, 張心韜 蘭亮 譯,更多章節內容可以訪問云棲社區“異步社區”公眾號查看。

2.7 __del__()方法

__del__()方法有一個讓人費解的使用場景。

這個方法的目的是在將一個對象從內存中清除之前,可以有機會做一些清理工作。如果使用上下文管理對象或者with語句來處理這種需求會更加清晰,這也是第5章“可調用對象和上下文的使用”的內容。對于Python的垃圾回收機制而言,創建一個上下文比使用__del__()更加容易預判。

但是,如果一個Python對象包含了一些操作系統的資源,__del__()方法是把資源從程序中釋放的最后機會。例如,引用了一個打開的文件、安裝好的設備或者子進程的對象,如果我們將資源釋放作為__del__()方法的一部分實現,那么我們就可以保證這些資源最后會被釋放。

很難預測什么時候__del__()方法會被調用。它并不總是在使用del語句刪除對象時被調用,當一個對象因為命名空間被移除而被刪除時,它也不一定被調用。Python文檔中用不穩定來描述__del__()方法的這種行為,并且提供了額外的關于異常處理的注釋:運行期的異常會被忽略,相對地,會使用sys.stderr打印一個警告。

基于上面的這些原因,通常更傾向于使用上下文管理器,而不是實現__del__()。

2.7.1 引用計數和對象銷毀

CPython的實現中,對象會包括一個引用計數器。當對象被賦值給一個變量時,這個計數器會遞增;當變量被刪除時,這個計數器會遞減。當引用計數器的值為0時,表示我們的程序不再需要這個對象并且可以銷毀這個對象。對于簡單對象,當執行刪除對象的操作時會調用__del__()方法。

對于包含循環引用的復雜對象,引用計數器有可能永遠也不會歸零,這樣就很難讓__del__()被調用。

我們用下面的一個類來看看這個過程中到底發生了什么。

class Noisy:def __del__( self ):print( "Removing {0}".format(id(self)) )

我們可以像下面這樣創建和刪除這個對象。

>>> x= Noisy()
>>>del x
Removing 4313946640

我們先創建,然后刪除了Noisy對象,幾乎是立刻就看到了__del__()方法中輸出的消息。這也就是說當變量x被刪除后,引用計數器正確地歸零了。一旦變量被刪除,就沒有任何地方引用Noisy實例,所以它也可以被清除。

下面是淺復制中一種常見的情形。

>>> ln = [ Noisy(), Noisy() ]
>>> ln2= ln[:]
>>> del ln

Python沒有響應del語句。這說明這些Noisy對象的引用計數器還沒有歸零,肯定還有其他地方引用了它們,下面的代碼驗證了這一點。

>>> del ln2
Removing 4313920336
Removing 4313920208

ln2變量是ln列表的一個淺復制。有兩個列表引用了Noisy對象,所以在這兩個列表被刪除并且引用計數器歸零之前,Python不會銷毀這兩個Noisy對象。

還有很多種創建淺復制的方法。下面是其中的一些。

a = b = Noisy()
c = [ Noisy() ] * 2

這里的關鍵是,由于淺復制在Python中非常普遍,所以我們往往對存在的對象的引用感到非常困惑。

2.7.2 循環引用和垃圾回收

下面是一種常見的循環引用的情形。一個父類包含一個子類的集合,同時集合中的每個子類實例又包含父類的引用。

下面我們用這兩個類來看看循環引用。

class Parent:def __init__( self, *children ):self.children= list(children)for child in self.children:child.parent= selfdef __del__( self ):print( "Removing {__class__.__name__} {id:d}".
format( __class__=self.__class__, id=id(self)) )
class Child:def __del__( self ):print( "Removing {__class__.__name__} {id:d}".
format( __class__=self.__class__, id=id(self)) )

一個Parent的instance包括一個children的列表。

每一個Child的實例都有一個指向Parent類的引用。當向Parent內部的集合中插入新的Child實例時,這個引用就會被創建。

我們故意把這兩個類寫得比較復雜,所以下面讓我們看看當試圖刪除對象時,會發生什么。

>>>> p = Parent( Child(), Child() )
>>> id(p)
4313921808
>>> del p

Parent和它的兩個初始Child實例都不能被刪除,因為它們之間互相引用。

下面,我們創建一個沒有Child集合的Parent實例。

>>> p= Parent()
>>> id(p)
4313921744
>>> del p
Removing Parent 4313921744

和我們預期的一樣,這個Parent實例成功地被刪除了。

由于互相之間有引用存在,因此我們不能從內存中刪除Parent實例和它包含的Child實例的集合。如果我們導入垃圾回收器的接口——gc,我們就可以回收和顯示這些不能被刪除的對象。

下面的代碼中,我們使用了gc.collect()方法回收所有定義了__del__()方法但是無法被刪除的對象。

>>> import gc
>>> gc.collect()
174
>>> gc.garbage
[<__main__.Parent object at 0x101213910>, <__main__.Child object at 0x101213890>, <__main__.Child object at 0x101213650>, <__main__.Parent object at 0x101213850>, <__main__.Child object at 0x1012130d0>, <__main__.Child object at 0x101219a10>, <__main__.Parent object at 0x101213250>, <__main__.Child object at 0x101213090>, <__main__.Child object at 0x101219810>, <__main__.Parent object at 0x101213050>, <__main__.Child object at 0x101213210>, <__main__.Child object at 0x101219f90>, <__main__.Parent object at 0x101213810>, <__main__.Child object at 0x1012137d0>, <__main__.Child object at 0x101213790>]

可以看到,我們的Parent對象(例如,4313921808的ID = 0x101213910)在不可刪除的垃圾對象列表中很突出。為了讓引用計數器歸零,我們需要刪除所有Parent對象中的children列表,或者刪除所有Child實例中對Parent的引用。

注意,即使把清理資源的代碼放在__del__()方法中,我們也沒辦法解決循環引用的問題。因為__del__()方法是在循環引用被解除并且引用計數器已經歸零之后被調用的。當有循環引用時,我們不能只是簡單地依賴于Python中計算引用數量的機制來清理內存中的無用對象。我們必須顯式地解除循環引用或者使用可以保證垃圾回收的weakref引用。

2.7.3 循環引用和weakref模塊

如果我們需要循環引用,但是又希望將清理資源的代碼寫在__del__()中,這時候我們可以使用弱引用。循環引用的一個常見場景是互相引用:一個父類中包含了一個集合,集合中的每一個實例也包含了一個指向父類的引用。如果一個Player對象中包含多個Hand實例,那么在每一個Hand對象中都包括一個指向對應的Player類的引用可能會更方便。

默認的對象間的引用可以被稱為強引用,但是,叫直接引用可能更好。Python的引用計數機制會直接使用它們,而且如果引用計數無法刪除這些對象的話,垃圾回收機器也能及時發現。它們是不可忽略的對象。

對一個對象的強引用就是直接引用,下面是一個例子。

當我們遇到如下語句。

a= B()

變量a直接引用了B類的一個對象。此時B的引用計數至少是1,因為a變量包含了一個指向它的引用。

想要找個一個弱引用相關的對象需要兩個步驟。一個弱引用會調用x.parent(),這個函數將弱引用作為一個可調用對象來查找它真正的父對象。這個過程讓引用計數器得以歸零,垃圾回收器可以回收引用的對象,但是不回收這個弱引用。

weakref定義了一系列使用了弱引用而沒有使用強引用的集合。它讓我們可以創建一種特殊的字典類型,當這種字典的對象沒有用時,可以保證被垃圾回收。

我們可以修改Parent和Child類,在Child指向Parent的引用中使用弱引用,這樣就可以簡單地保證無用對象會被銷毀。

下面是修改后的類,它在Child指向Parent的引用中使用了弱引用。

import weakref
class Parent2:def __init__( self, *children ):self.children= list(children)for child in self.children:child.parent= weakref.ref(self)def __del__( self ):print( "Removing {__class__.__name__} {id:d}".format( __class__= self.__class__, id=id(self)) )

我們將child中的parent引用改為一個weakref對象的引用。

在Child類中,我們必須用上面說的兩步操作來定位parent對象:

p = self.parent()
if p is not None:# process p, the Parent instance
else:# the parent instance was garbage collected.

我們可以顯式地確認引用的對象是否已經找到,因為有可能該引用已經變成虛引用。

當我們使用這個新的Parent2類時,可以看到引用計數成功地歸零同時對象也被刪除了:

>>> p = Parent2( Child(), Child() )
>>> del p
Removing Parent2 4303253584
Removing Child 4303256464
Removing Child 4303043344

當一個weakref引用變成死引用時(因為引用被銷毀了),我們有3個可能的方案。

  • 重新創建引用對象,或重新從數據庫中加載。
  • 當垃圾回收器在低內存情況下錯誤地刪除了一些對象時,使用warnings模塊記錄調試信息。
  • 忽略這個問題。

通常,weakref引用變成死引用是因為響應的對象已經被刪除了。例如,變量的作用域已經執行結束,一個沒有用的命名空間,應用程序正在關閉。對于這個原因,通常我們會采取第3種響應方法。因為試圖創建這個引用的對象時很可能馬上就會被刪除。

2.7.4 __del__()和close()方法

__del__()最常見的用途是確保文件被關閉。

通常,包含文件操作的類都會有類似下面這樣的代碼。

__del__ = close

這會保證__del__()方法同時也是close()方法。

其他更復雜的情況最好使用上下文管理器。詳情請看第5章“可調用對象和上下文的使用”,我們會在第5章提供更多和上下文管理器有關的信息。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/541458.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/541458.shtml
英文地址,請注明出處:http://en.pswp.cn/news/541458.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

NullReferenceException C#中的異常

什么是NullReferenceException&#xff1f; (What is NullReferenceException?) NullReferenceException is an exception and it throws when the code is trying to access a reference that is not referencing to any object. If a reference variable/object is not refe…

java map key 大寫轉小寫_Spring JdbcTemplate 查詢出的Map,是如何產生大小寫忽略的Key的?(轉)...

Java 是區分大小寫的&#xff0c;普通的Map例如HashMap如果其中的key"ABC" value"XXX"那么map.get("Abc") 或 map.get("abc")是獲取不到值得。但Spring中產生了一個忽略大小寫的map使我產生了好奇例如 jdbcTemplate.queryForList(sql)…

《iOS 6核心開發手冊(第4版)》——2.11節秘訣:構建星星滑塊

本節書摘來自異步社區《iOS 6核心開發手冊&#xff08;第4版&#xff09;》一書中的第2章&#xff0c;第2.11節秘訣&#xff1a;構建星星滑塊&#xff0c;作者 【美】Erica Sadun&#xff0c;更多章節內容可以訪問云棲社區“異步社區”公眾號查看 2.11 秘訣&#xff1a;構建星星…

css框架和js框架_優雅設計的頂級CSS框架

css框架和js框架Brief discussion: 簡要討論&#xff1a; Well, who doesnt want their website or web page to look attractive, stylish and be responsive? 那么&#xff0c;誰不希望自己的網站或網頁看起來有吸引力&#xff0c;時尚并且ReactSwift&#xff1f; We put …

軟考下午題具體解釋---數據流圖設計

在歷年的軟考下午題其中&#xff0c;有五道大題。各自是數據流圖的設計&#xff0c;數據庫設計&#xff0c;uml圖&#xff0c;算法和設計模式&#xff0c;從今天這篇博文開始&#xff0c;小編就跟大家來一起學習軟考下午題的相關內容。包含理論上的知識以及典型例題的解說&…

基本程序 打印Scala的Hello World

Scala中的基本程序 (Basic program in Scala) As your first Scala program, we will see a basic output program that just prints "Hello World" or any other similar type of string. With this example, we will see what are the part of the code that is im…

java treemap lastkey_Java TreeMap lastKey()用法及代碼示例

java.util.TreeMap.lastKey()用于檢索Map中存在的最后一個或最高鍵。用法:tree_map.lastKey()參數&#xff1a;該方法不帶任何參數。返回值&#xff1a;該方法返回映射中存在的最后一個鍵。異常&#xff1a;如果映射為空&#xff0c;則該方法將引發NoSuchElementException。以下…

mysql屬于數據庫三級模式_數據庫系統的三級模式指的是什么

數據庫系統的三級模式指的是什么發布時間&#xff1a;2020-10-26 10:11:21來源&#xff1a;億速云閱讀&#xff1a;52作者&#xff1a;小新小編給大家分享一下數據庫系統的三級模式指的是什么&#xff0c;希望大家閱讀完這篇文章后大所收獲&#xff0c;下面讓我們一起去探討吧&…

《自頂向下網絡設計(第3版)》——導讀

目錄 第1部分 辨明客戶的需求和目標 第1章 分析商業目標和制約 1.1 采用自頂向下的網絡設計方法 1.2 分析商業目標 1.3 分析商業制約 1.4 商業目標檢查表 1.5 小結 1.6 復習題 1.7 設計環境 第2章 分析技術目標與折衷措施 2.1 可擴展性 2.2 可用性 2.3 網絡性能 2.4 安全性 2…

python矩陣變化_用numpy改變矩陣的形狀

我的問題有兩個方面。我有下面的代碼來處理一些矩陣。在import numpytupleList [(0, 122), (1, 246), (2, 157), (3, 166), (4, 315), (5, 108), (6, 172), (7, 20), (8, 173), (9, 38), (10, 28), (11, 72), (12, 102), (13, 277), (14, 318), (15, 316), (16, 283), (17, 31…

最小硬幣問題_進行更改的最小硬幣數量

最小硬幣問題Description: 描述&#xff1a; This is classic dynamic programming problem to find minimum number of coins to make a change. This problem has been featured in interview rounds of Amazon, Morgan Stanley, Paytm, Samsung etc. 這是經典的動態編程問題…

java 生成xml亂碼_jdom解決中文亂碼問題 JAVA生成xml文件幫了我很大的忙

決解了數據庫讀取出來 再保存到xml 產生的亂碼問題import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStreamWriter;import org.jdom.Attribute;import org.jdom.Document;import org.jdom.Element;import org.jdom.output.Format;import org.…

給定重量上限,背包問題_滿足給定重量的袋子的最低成本

給定重量上限,背包問題Problem statement: 問題陳述&#xff1a; You are given a bag of size W kg and you are provided costs of packets different weights of oranges in array cost[] where cost[i] is basically cost of i kg packet of oranges. cost[i] -1 means t…

springMVC rest風格

1.dispatcherServlet的配置<!-- The front controller of this Spring Web application, responsible for handling all application requests --><servlet><servlet-name>springDispatcherServlet</servlet-name><servlet-class>org.springfram…

sql2008能否打開mysql數據庫_mysql數據庫數據能不能導入到sql server中

點“測試”按鈕確認你的鏈接是正確的。 Press the "Test" button to ensure your connection settings are set properly and then the "OK" button when youre done.二. 創建Microsoft SQL到MySQL的鏈接1.在SQL Server Management Studio中打開一個new qu…

c語言 函數的參數傳遞示例_isunordered()函數與C ++中的示例

c語言 函數的參數傳遞示例C isunordered()函數 (C isunordered() function) isunordered() function is a library function of cmath header, it is used to check whether the given values are unordered (if one or both values are Not-A-Number (NaN)), then they are u…

java進一_JAVA小白進:基礎入門知識

1.注釋&#xff0c;關鍵字&#xff0c;標識符1.注釋(1)注釋&#xff1a;解釋說明程序的而文字。(2)注釋的分類&#xff1a;單行注釋 格式&#xff1a; //注釋的文字多行注釋 格式&#xff1a;/*注釋的文字*/文檔注釋 格式&#xff1a;/**注釋的文字*/(3)注釋的作用&#xff1a;…

補丁(patch)的制作與應用

為什么80%的碼農都做不了架構師&#xff1f;>>> 轉自http://linux-wiki.cn/wiki/zh-hans/%E8%A1%A5%E4%B8%81(patch)%E7%9A%84%E5%88%B6%E4%BD%9C%E4%B8%8E%E5%BA%94%E7%94%A8 如果hack了開源代碼&#xff0c;為了方便分享&#xff08;如提交Bug&#xff09;或自己…

php知識點匯總與解答_PHP操作員能力傾向問題與解答

php知識點匯總與解答This section contains Aptitude Questions and Answers on PHP Operators. 本節包含有關PHP運算符的 Aptitude問答。 1) Which of the following types of operators are used in PHP? Arithmetic OperatorsLogical OperatorsArray OperatorsString Oper…

csv導入mysql phpmyadmin_【轉】從phpMyAdmin批量導入Excel內容到MySQL(親測非常簡潔有效)...

今天做項目遇到需要用phpMyAdmin批量導入Excel內容到MySQL數據庫。分析了我的踏坑經歷并且總結一最便捷的一套導入數據的方法&#xff0c;非常實用簡潔&#xff1a;1、修改Excel表的數據&#xff0c;使得Excel中的字段與數據庫字段要一一對應&#xff0c;并加上自增id。2、然后…