a.为什么要比较上下文管理和构造析构函数
最近接手一个Python项目代码,刚接手没几天,就被反馈存在一个bug:在一个常驻进程里,每个任务需要几个文件句柄来进行日志重定向,但使用完后没有对文件进行关闭操作,导致文件句柄过多,出现异常。
于是乎打算使用上下文管理器去优化代码,解决文件句柄未及时关闭的问题。然而这样做,需要修改的代码量太大,因此未采纳该方案,后来有人建议在(自定义的)logger类里面的析构函数里对文件句柄进行关闭操作,
因为这个logger类都是以局部变量的形式创建的,因此在函数执行完毕后,就会自动调用局部变量的析构函数,这样就能在不需要大量修改代码的前提下修复这个bug。
这就引发我的一个思考,如果构造析构函数也能做到上下文管理器所做的事情,而且不需要使用特别的初始化对象代码,为什么我们还需要上下文管理器呢?谷歌后也发现不少人有和我一样的疑惑,所以,查阅一堆资料后记下此文。
b.什么是上下文管理器
首先啰嗦地介绍一下Python的上下文管理器。
用过Python的都知道,Python有一个特别的关键在with,最常见的场景就是用它来打开一个文件。
如果是其他语言,打开一个文件对象,最后是要关闭的。当然不使用with来创建对象的话,python的代码最后也是需要调用文件对象的的close函数,
但如果你用了with,简单地说,就不需要你处理后续的收尾工作了,with关键字给开发者带来了十分大的便利。
其实with关键字是Python数据模型的一种特性,称为上下文管理器,并非所有类都可以使用with实例化,它的工作原理类实现了__enter__和__exit__两个函数,
在通过with实例化时,执行__enter__函数,并在with代码块结束时执行__exit__函数,举个栗子:
我们通过with创建了一个文件对象writer,并在with代码块中调用了writer的write函数,我们不需要再调用writer的close函数,因为with代码块结束时,__exit__函数里就为我们调用了该函数。
更让我们省心的是,我们不需要担心因为with代码块中产生异常导致close函数未被执行,因为不管程序是正常执行还是抛出异常,只要离开with代码块,__exit__函数就会被调用。
因此以上代码等同于:
这么看,代码是不是变得相当简洁?而且再也不怕忘记关闭文件了。
除了文件,上下文管理器还可以用于全局状态、数据库、锁等一系列需要保存恢复、打开关闭操作的资源。更多有关上下文管理器的知识请看官方文档https://docs.python.org/3/library/stdtypes.html#typecontextmanager
c.什么是构造析构函数
是个猿都知道构造函数和析构函数,虽然随着计算机语言越来越高级,析构函数的概念已然变得模糊,但是其实每种语言中基本都能找到构造析构函数的对应实现,析构函数如java中的finalize、python中的__del__、scala的unapply、php的__destruct等等,当然一般我们都不需要管它。
构造函数就是为对象初始化所做的一系列操作,python类的构造函数为__init__,这里不做过多说明,不了解的请看https://docs.python.org/3/reference/datamodel.html?highlight=destruct#object.__init__
析构函数的主要目的是销毁对象、释放内存,因此在局部变量的声明周期结束时,Python语言是会自动调用对象的析构函数。但是这里要注意,使用del关键字不能保证对象被销毁。因为del关键字只是干掉当前变量名,并取消该变量对对象的引用,
而python对象的生命周期取决于它的引用数,只有它的引用为0时,才会真正调用该析构函数。
领取专属 10元无门槛券
私享最新 技术干货