1、del函数的定义
python中的 del 函数,仅仅为解除变量与对象之间的引用关系,并将对象的引用减一(此处不讨论对数组、字典成员的删除操作)。我们知道,一个对象被实例化之后,可以与一个或多个变量名绑定,每绑定一个变量名,实例化的对象引用加一。python在实例化对象的引用为0时,则gc会自动回收这个实例化的对象。那么什么时候对象的引用会被减一,有几种方式:1、变量超出了作用域。2、通过del 删除了变量对实例的引用。对于超过作用域我们很好理解,例如一个函数内的局部变量引用了一个函数里实例化的对象,跳出函数后,变量便超出作用域失效,这时实例的引用计数也随之减一。对于del 删除变量对实例的引用,这个便是本文着重讲述的内容。
2、删除实例引用操作
#--*--coding:utf-8--*--
class LiveObject:
def __init__(self):
print("LiveObject init")
def add(self,a:int,b:int)->int:
return a+b
def __del__(self):
print("LiveObject del")
if __name__ == "__main__":
live = LiveObject()
slink = live
del live
#print(live)
print(slink.add(1,2))
input("Press any key to exit...")上例中,我们通过 live = LiveObject() 这条语句实例化了一个LiveObject对象,并赋值给live这个变量。这个时候,LiveObject实例化的对象的引用+1,接下来的依据slink=live,则是将live赋值给slink,实际上也就是slink也指向了实例化的LiveObject对象。此时 LiveObject实例引用再+1。接下来执行del live这句话以后,如果继续执行 print(live) 程序会报live变量未定义的错误。 为什么会报错,也就是del live这句话,解除了变量live与LiveObject实例的引用关系。此时live是无意义的一个符号。
接下来 print(slink.add(1,2)) 这条语句执行是成功的。为什么呢?当slink = live时,此时slink已经指向了LiveObject实例,并不是指向live这个变量,所以即便是live变量不存在了,而LiveObject的实例还存在(为什么还存在,因为之前对象的引用增加了两次,del live删除了引用减小了1次,那么LiveObject的引用还剩下一次,也就是slink对他的引用,此时gc还不会回收LiveObject实例)。所以调用是合法的。
接下来继续执行,输入任意键后,整个程序退出。之前所说的变量超出作用域会自动删除,这个时候slink也将被删除。此时,会打印出“LiveObject del“内容。为什么呢?这是因为slink被删除时,LiveObject实例的引用再次被减一,这个时候LiveObject的引用为0,当实例的引用为0时,gc会自动回收对象,回收对象的时候触发了对象的__del__函数,所以才打印出“LiveObject del”字样。
3、类的构造和析构函数
通过以上的代码例子可以看出,对象释放的时候,gc会调用类的__del__函数。学过C++的人都知道,也就相当于析构函数。但是,通过我多年的使用,在此真的不太建议大家将一些资源释放的操作放在__del__中。因为实例在释放是python gc自动完成的,而gc回收对象的条件是引用为0。在实际开发过程中,逻辑中有些时候对对象的引用很复杂,存在先后顺序、常驻问题等等。有时对象的引用直到程序结束前都不会被归零,所以很难触发到__del__函数(当然,你要是特别牛的大神,逻辑清晰、思路清奇另当别论)。这样就导致该合理释放的东西,没有在你预期的时候释放(例如:在__del__中关闭一个独占模式打开的文件),这样会导致后期对资源的访问产生错误。在此,我个人比较喜欢的形式是通过with 来控制。
我们把上面的例子改造一下,来看看with的控制方式:
#--*--coding:utf-8--*--
class LiveObject:
__cr:str = ""
def __init__(self,cr:str):
self.__cr = cr
print(f"LiveObject {self.__cr} init")
def __enter__(self):
print(f"LiveObject {self.__cr} enter")
return self
def add(self,a:int,b:int)->int:
return a+b
def __exit__(self,type, value, trace):
print(f"LiveObject {self.__cr} exit")
def __del__(self):
print(f"LiveObject {self.__cr} del")
if __name__ == "__main__":
with LiveObject("i1") as live_object:
print(live_object.add(1,2))
with LiveObject("i2") as live_object1:
print(live_object1.add(1,2))
input("Press any key to exit...")实现支持 with 操作的类中,包含有__enter__和__exit__两个函数,分别对应通过with 实例化对象和超出with作用域后退出(释放)对象的函数。上例中,__enter__函数返回了它本身,那么with LiveObject("i1") as live_object:这一句就是实例化LiveObject对象并赋值给live_object变量,然后作用域内进行了add的操作。当然,需不需要返回你决定,如果仅仅实例化就能完成整个操作,而不用作用域内进行其他调用,也可以不用返回self。以上代码执行的结果是:
LiveObject i1 init LiveObject i1 enter 3 LiveObject i1 exit LiveObject i2 init LiveObject i2 enter 3 LiveObject i2 exit Press any key to exit...
可以看出,with实例化对象后会调用__enter__函数,超出with作用域后会自动调用__exit__函数。所以,在一个类里面,对资源进行初始化,然后在类释放时析构资源,采用with是非常安全的方式。比del操作的风险小的多。
好了,暂且说到这里,有什么疑问或者建议,望大家讨论。
备案/许可证编号:

wishst