一、 引言

基本上所有支持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. 家庭版window10找不到文件'gpedit.msc'。请确定文件名是否正确后 ,再试一次

    今天遇到电脑找不到gpedit.msc文件,所以记录一下这个问题的解决方法 1. 首先建立一个空白文档  代码如下: @echo off pushd "%~dp0" dir /b ...

  2. Electron入门指北

    最近几年最火的桌面化技术,无疑是Qt+和Electron. 两者都有跨平台桌面化技术,并不局限于Windows系统.前者因嵌入式而诞生,在演变过程中,逐步完善了生态以及工具链.后者则是依托于Node. ...

  3. python_udp_多人聊天室_简单版

    udp-一定是client端先发送数据. server.py import socket friend_lst = {'alex':'32','太白':'33'} sk =socket.socket( ...

  4. CORS跨域请求:前后端分离

    1. 请求过滤器: /** * OncePerRequestFilter保证在任何Servlet容器中都是一个请求只执行一次的过滤器. */ public class CorsFilter exten ...

  5. 在Docker上部署自动更新ssl证书的nginx + .NET CORE

    突发奇想要搞一个ssl的服务器,然后我就打起了docker的主意,想着能不能搞一个基于Docker的服务器,这样维护起来也方便一点. 设想 想法是满足这么几点: .NET CORE on Docker ...

  6. Elasticsearch 第七篇:父子结构mapping设计以及相关查询

    h2.post_title { background-color: rgba(43, 102, 149, 1); color: rgba(255, 255, 255, 1); font-size: 1 ...

  7. http代理阅读2

    向上游服务器发送请求处理 static void ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t * ...

  8. select的限制

    /*一.select实现并发服务器并发的两点限制 1.一个进能够打开的最大文件描述符限制.可以通过两种方式修改 ulimit -n :获取最大文件描述符个数 ulimit -n 2048:修改为204 ...

  9. 基于FFmpeg的Dxva2硬解码及Direct3D显示(三)

    初始化Direct3D 目录 初始化Direct3D 创建Direct3D物理设备对象实例 创建Direct3D渲染设备实例 创建Direct3D视频解码服务 Direct3D渲染可以通过Surfac ...

  10. rbd的增量备份和恢复

    前言 快照的功能一般是基于时间点做一个标记,然后在某些需要的时候,将状态恢复到标记的那个点,这个有一个前提是底层的东西没用破坏,举个简单的例子,Vmware 里面对虚拟机做了一个快照,然后做了一些系统 ...