一、 引言

基本上所有支持OOP设计的语言都支持析构方法(也称析构函数),析构方法都是在对象生命周期结束时调用,一般用来实施实例相关生命周期内访问数据的扫尾工作,包括关闭文件、释放内存、输出日志、清理数据等。

二、 析构方法语法

Python中所有类的析构方法都是特殊方法__del__,析构方法同样是一个实例方法,其语法如下:

del(self)

self就是对象自身,所有实例方法都有该参数,真正调用时无需传递。

析构方法没有返回值要求。

析构方法语法很简单,没有需要过多解释的地方。

三、 析构方法的使用

  1. 析构方法比较特殊,这是因为它是Python中定义的特殊方法,在对象销毁时自动执行,但在object类内却没有该方法,在自定义类定义时,并没有要求一定要自己定义析构方法,因此在自定义类及其自定义父类中没有定义__del__析构方法时,该类就没有析构方法;
  2. 如果子类定义了析构方法,其派生的父类也定义了析构方法,则在子类的析构方法中必须调用父类的析构方法;
  3. 当程序不再需要一个 Python 对象时,系统必须把该对象所占用的内存空间释放出来,这个过程被称为垃圾回收(GC,Garbage Collector),Python 会自动回收所有对象所占用的内存空间,开发者无须关心对象垃圾回收的过程。垃圾回收会自动触发对析构方法的调用;
  4. 使用del删除实例对象时,并不一定会触发析构方法的调用

    1>Python中可能存在多个实例ID相同的情况,如实例a通过赋值语句赋值给b时,此时a与b的ID相同,就认为该ID对象上增加了一个引用;如果一个对象有多个变量引用它,那么 del 其中一个变量是不会回收该对象的。这个销毁的动作还是需要等待对象引用没有了以后才能够完成。

    2>Python 采用自动引用计数(ARC:Automatic Reference Counting)方式来判断对象是否需要被回收,当程序中有一个变量引用该对象时,Python 会自动设置该对象引用计数为1;当程序中每增加一个变量引用该 Python 对象(如赋值)时,Python 会自动对该对象引用计数加1,反之如果每减少一个(如del 变量)变量的引用,该对象引用计数就减一,当对象的引用计数变成 0,则说明不再有变量引用该对象,Python 就会回收该对象,此时才会调用析构方法;

    3>大部分时候,Python 的 ARC 都能准确、高效地回收系统中的每个对象。但如果系统中出现循环引用的情况,如两个对象下的属性相互指向对方(如双向链表),但两个对象都没有单独的变量引用它们,此时两个对象的引用计数并不是0,而都是1,但实际上程序已经不再有变量引用它们,此时 Python 的垃圾回收器就可能没那么快,要等专门的循环垃圾回收器(Cyclic Garbage Collector)来检测并回收这种引用循环。
  5. 由于调用 del() 方法时周边状况已不确定,如部分关联对象已经回收、部分模块删除了等,因此Python对在__del__() 方法执行期间发生的异常将被忽略,改为打印一个警告到 sys.stderr;
  6. 老猿验证在交互模式执行del实例变量时,哪怕对象没有引用了,Python也不一定马上执行垃圾回收和__del__方法,会出现比较复杂的情况,可能在del语句执行一段时间后执行其他语句时突然释放。对此老猿不展开多说,为此老猿下面的代码是通过文件方式执行的。

    四、 例子
  7. 案例说明

    本节定义Vehicle类和Car类,后者是从前者派生的。将在定义实例后,将实例赋值给另一个变量,然后使用del删除变量,看看是否能触发析构方法。
  8. 源代码
class Vehicle():
def __init__(self,wheelcount,power):
self.wheelcount = wheelcount
self.power = power
print("In Vehicle init,objectid = {:#016X}".format(id(self)))
def __del__(self):
print("in Vehicle __del__,delete objectid = {:#016X}".format(id(self)))
print("Vehicle deleted") class Car(Vehicle):
def __init__(self, power,oilcostper100km):
print("In Car init,objectid = {:#016X}".format(id(self)))
self.oilcostper100km = oilcostper100km
super().__init__(4, power) def __del__(self):
print("in Car __del__,delete objectid = {:#016X}".format(id(self)))
super().__del__()
print("Car deleted") print("execut Car('汽油发动机',10)...")
car = Car('汽油发动机',10) #对象引用数为1
c=car #对象引用数为2
print("execut del car...")
del car #对象引用数为1
print("execut del c...")
del c #对象引用数为0,应该触发析构方法
print("Prog end.")
  1. 源代码截图:

  2. 执行结果截图:

  3. 案例总结

    通过上述截图可以看出,在引用数为0才触发了析构方法。

    最后,老猿需要强调一点,在Python中析构方法的使用要慎重,一是因为del变量不一定触发析构方法,二是因为调用 del() 方法时周边状况已不确定,三是Python有自己的垃圾回收机制执行相关资源的回收和清理,因此编程者意图在析构方法中实施的操作不一定会按照编程者的意图完整实施。

    本节对Python析构方法的语法、使用以及注意事项进行了深入介绍,并举例进行了验证,请大家理解,并慎重使用!

老猿Python,跟老猿学Python!

博客地址:https://blog.csdn.net/LaoYuanPython


请大家多多支持,点赞、评论和加关注!谢谢!

第8.18节 Python类中内置析构方法__del__的更多相关文章

  1. 第8.9节 Python类中内置的查看直接父类的__bases__属性

    终于介绍完了__init__方法和__new__方法,接下来轻松一下,本节介绍类中内置的__bases__属性. 一. 语法释义 Python 为所有类都提供了一个 bases 属性,通过该属性可以查 ...

  2. 第8.14节 Python类中内置方法__str__详解

    一. object类内置方法__str__和函数str 类的内置方法__str__和内置函数str实际上实现的是同一功能,实际上str调用的就是__str__方法,只是调用方式不同,二者的调用语法如下 ...

  3. 第8.13节 Python类中内置方法__repr__详解

    当我们在交互环境下输入对象时会直接显示对象的信息,交互环境下输入print(对象)或代码中print(对象)也会输出对象的信息,这些输出信息与两个内置方法:__str__方法和__repr__方法有关 ...

  4. python - 类的内置 attr 方法

    类的内置 attr 方法 #类的内置 attr 方法: # __getattr__ # __setattr__ # __delattr__ # __getattr__ #到调用一个类不存在数参数时,将 ...

  5. python -- 类中--内置方法

    isinstance 和  issubclass isinstance(obj,b)  检查是否obj是否是类b的对象 class A(object):pass class B(A):pass b=B ...

  6. 第7.17节 Python类中的静态方法装饰器staticmethod 定义的静态方法深入剖析

    第7.17节  Python类中的静态方法装饰器staticmethod 定义的静态方法深入剖析 静态方法也是通过类定义的一种方法,一般将不需要访问类属性但是类需要具有的一些能力可以静态方法提供. 一 ...

  7. 第7.14节 Python类中的实例方法详析

    第7.14节 Python类中的实例方法详析 一.    实例方法的定义 在本章前面章节已经介绍了类的实例方法,实例方法的定义有三种方式: 1.    类体中定义实例方法 第一种方式很简单,就是在类体 ...

  8. 第8.6节 Python类中的__new__方法深入剖析:调用父类__new__方法参数的困惑

    上节<第8.5节 Python类中的__new__方法和构造方法__init__关系深入剖析:执行顺序及参数关系案例详解>通过案例详细分析了两个方法的执行顺序,不知大家是否注意到了,在上述 ...

  9. 第8.12节 Python类中使用__dict__定义实例变量和方法

    上节介绍了使用实例的__dict__查看实例的自定义属性,其实还可以直接使用__dict__定义实例变量和实例方法. 一. 使用__dict__定义实例变量 语法: 对象名. dict[属性名] = ...

随机推荐

  1. 一次打包引发的思考,原来maven还能这么玩?

    持续原创输出,点击上方蓝字关注我 目录 前言 依赖关系 你会怎么做? 必知的几个参数 总结 前言 昨天有一个读者找我的交流工作心得,偶然间提到一个有趣的问题,如下: 「大致的意思」:公司最近在整多模块 ...

  2. Mongodb和Hbase的对比

    Mongodb和Hbase的对比 1.Mongodb bson文档型数据库,整个数据都存在磁盘中,hbase是列式数据库,集群部署时每个familycolumn保存在单独的hdfs文件中. 2.Mon ...

  3. JavaScript 读取CSS3 transform

    某些场景需要读取 css3 transform的属性 例如 transform:translate(10px,10px) rotate(-45deg); 这该怎么读取呢,正则表达式?毫无疑问这很坑爹 ...

  4. HotSpot的启动过程(配视频进行源码分析)

    本文将详细介绍HotSpot的启动过程,启动过程涉及到的逻辑比较复杂,细节也比较多,为了让大家更快的了解这部分知识,我录制了对应的视频放到了B站上,大家可以参考. 第4节-HotSpot的启动过程 下 ...

  5. js 及jQery

    1.jQuery即是对象也是类. 2.append()方法的使用,连加操作. 例子: $(function(){ var obj=new Object(); obj.id=$("input[ ...

  6. solr 笔记

    1.sorl其实是对存储的内容,根据相应的域和域的类型先分词,停顿,过滤(大小写转换)等等;然后建立多级索引.对搜索条件也是根据相应的域和域的类型进行分词,停顿,同义词,过滤(大小写转换)等等;然后建 ...

  7. ceph osd tree的可视化

    前言 很久没有处理很大的集群,在接触一个新集群的时候,如果集群足够大,需要比较长的时间才能去理解这个集群的结构,而直接去看ceph osd tree的结果,当然是可以的,这里是把osd tree的结构 ...

  8. Ceph部署的时候修改默认权重

    前言 部署集群的时候权重是默认生成的,这个是根据磁盘大小分配的,我们有的时候需要去修改一下这个默认权重 修改 如果统一的初始值,那么直接添加参数即可 osd_crush_initial_weight ...

  9. go get以后下载的包不在src下而在pkg的问题

    我的GOPATH是这样的 但是当我go get下载包之后 下载的却不在src,而是在 $GOPATH$/pkg 下 原因可能是之前第一次go get下载, GitHub的速度太慢了,我更改了代理,使用 ...

  10. TypeScript 引入第三方包,报无法找到模块错误

    以 react-router-dom 模块为例 1. npm加上 @types/ 根据报错提示尝试安装该库的TypeScript版本 (该库的 ts 声明文件),也就是在该库的名称前加上 @types ...