解决方案-关于python在析构函数中无法使用open函数

本文探讨了在Python中析构函数__del__中不能直接使用open()的原因,提供了包括try-except-finally、转移open函数、atexit模块和显式del等解决方案,并深入解析了垃圾回收机制。
摘要由CSDN通过智能技术生成

解决方案-关于python在析构函数中无法使用open函数

class Test:
    def __init__(self):
        print("__init__")
    def main(self):
        print("main")
    def __del__(self):
        with open("test.txt", "w") as f:
            f.write("hhhhh")
        print("__del__")

t = Test()
t.main()

会报错 NameError: name ‘open’ is not defined

python2没有这个问题

以下列出的一些解决方案和看法,但造成错误的具体/根本原因目前还未知
本文围绕该问题初步探究Python垃圾清理的机制

解决方案一 try …except …finally

情景: 我的需求是在对象销毁时, 要在文件中进行一个记录

解决: 使用 try...except...finally...
try:  # 在Test类中移除open()函数, 实例化对象交由try
    t = Test()
    t.main()
except Exception as e:
    print(e)
finally:  # open()函数交由finally
    with open("test.txt", "w") as f:
        f.write("20201214")
解决方案二 转移open()函数

在__del__中无法使用open()函数, 将open()转移到其它对象方法中, 最后在析构函数中关闭

class Test:
    def __init__(self):
        print("__init__")
    def main(self):
        print("main")
        self.file = open("test.txt", "w")
        
    def __del__(self):
        self.file.write("helloworld")
        print("__del__")
        self.file.close()

t = Test()
t.main()
解决方案三 使用atexit模块

在百度和必应中搜寻无果, 果然还是google更加强大!

为解决这一问题, 我们可以首先导入atexit模块, 然后将析构操作写入另一个函数中, 最后用atexit.register来注册这个函数. 比如, 以上程序可以改写如下:

import atexit
 
class 存储代理:
	def __init__(self):
		atexit.register(self.cleanup)
		print("开始")
 
	def cleanup(self):
		print("打开文件前")
		with open("./测试.txt", "w") as 文件:
			pass
		print("打开文件后")
    def __del__(self):
        pass
 
代理 = 存储代理()
atexit模块
atexit-允许程序员定义在正常程序终止时执行的多个退出函数。 at exit python atexit 模块定义了一个 register 函数,用于在 python 解释器中注册一个退出函数,这个函数在解释器正常终止时自动执行,一般用来做一些资源清理的操作。

atexit 按注册的相反顺序执行这些函数; 例如注册A、B、C,在解释器终止时按顺序C,B,A运行。

Note:如果程序是非正常crash,或者通过os._exit()退出,注册的退出函数将不会被调用。

atexit.register(func, *args, **kargs)

将func作为要在终止时执行的函数。任何要传递给func的可选参数必须作为参数传递给register() 。可以多次注册相同的函数和参数。

当程序退出的时候,按先进后出的顺序调用注册的函数。如果退出函数在执行过程中抛出了异常,atexit会打印异常的信息,并继续执行下一下回调,直到所有退出函数执行完毕,它会重新抛出最后接收到的异常。

一.通过装饰器使用atexit

from atexit import register
def main():
    print('Do something.')

@register
def _atexit():
    print('Done.')
    
if __name__ == '__main__':
 main()

输出: Do something

​ Done

二.非装饰器方式

from atexit import register
def main():
    print('main')
def goodbye(name, adjective):
    print('Goodbye, %s, it was %s to meet you.' % (name, adjective))
  
register(goodbye, 'Donny', 'nice')
# register(goodbye, adjective='nice', name='Donny')
if __name__ == '__main__':
	main()

输出: main
Goodbye, Donny, it was nice to meet you.

解决方案五 显式del
class Test:
    def __init__(self):
        print("__init__")
    def __del__(self):
        with open("xianshi.txt", "w") as f:
            f.write("ffff") 

t = Test()
del t
基于本例的问题探究

dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。

所以, 利用dir()函数, 我想探究在析构函数中到底减少了哪些方法和属性

class Test:
    def __init__(self):
        print(dir(__builtins__), len(dir(__builtins__)))
        print("__init__")

    def __del__(self):
        print("---del---")
        print(dir(__builtins__), len(dir(__builtins__)))
        print("---del---")
        with open("test.txt", "w") as f:
            f.write("ffff") 

t = Test()
# del t

结果分析: 在__init__(self)中, 内建属性和方法有153个, 然而在__del__中只有146个, 减少了7个

我使用集合经行相减后发现, 缺失的内建有

{'quit', 'license', 'credits', 'exit', 'help', 'open', 'copyright'}

在交互式的python中, 内建属性方法有154个

如果上述代码的第14行取消注释, 在析构函数中的内建属性和方法仍然有153个

个人看法
如果 手动del对象, 此时python内的资源还健全,

要是等到python解释器来清理, 它已经清理了太多资源,所以当执行到一些已清理的资源时便会报错

解决方案六
class Iam(object):

    def __init__(self):
        print("I m born")

    def __del__(self):
        #"open" function still in __builtins__ 
        f = open("memory_report.txt", "w")
        f.write("He gone safe")
        f.close()

def write_iam():
    print("----")
	i=Iam()
    print("====")

if __name__ == '__main__':
    write_iam()
    print("Script Ends. Now to GC clean memory")

主体程序结束后才调用 Iam类__del__方法,而不是当 i超出 write_iam的作用域时就去立即调用__del__方法。

原理如下:

Python 采用的是 引用计数机制为主, 标记-清除和分代收集两种机制为辅的策略。 因此, 当 Python 在超出范围时不会清理它,只有当它的最后一次引用超出范围时, 才会将它清理掉。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值