文件的类包装 - 在不再引用时关闭文件句柄的正确方法

Spe*_*bun 10 python garbage-collection resource-cleanup

我有一个包含我需要的文件处理功能的类.另一个类创建了一个实例,filehandler并在不确定的时间内使用它.最终,它caller被摧毁了,它摧毁了唯一的引用filehandler.

filehandler关闭文件的最佳方法是什么?

我目前使用__del__(self)但在看到几个 不同的 问题 和文章后,我认为这被认为是一件坏事.

class fileHandler:
    def __init__(self, dbf):
        self.logger = logging.getLogger('fileHandler')
        self.thefile = open(dbf, 'rb')
    def __del__(self):
        self.thefile.close()
Run Code Online (Sandbox Code Playgroud)

这是处理程序的相关部分.该类的重点是抽象出使用底层文件对象的细节,并避免不必要地将整个文件读入内存.但是,处理底层文件的一部分是在对象超出范围时关闭它.

caller不应该知道或关心参与的细节filehandler.filehandler当它超出范围时,释放所涉及的任何必要资源是我们的工作.这是它首先被抽象的原因之一.所以,我似乎面临着将filehandler代码移动到调用对象或处理漏洞抽象的问题.

思考?

Eth*_*man 14

__del__本身并不是一件坏事.您必须格外小心,不要在已__del__定义的对象中创建引用循环.如果您确实发现自己需要创建循环(父级指的是引用父级的子级),那么您将需要使用该weakref模块.

所以,__del__没关系,只要警惕cylic引用.

垃圾收集:这里最重要的一点是,当一个对象超出范围时,它可以被垃圾收集,而事实上,它被垃圾收集......但什么时候?不能保证何时,不同的Python实现在这个领域有不同的特性.因此,对于管理资源,最好是明确地添加.close()您的filehandler或者,如果您的使用是兼容的,添加__enter____exit__方法.

__enter____exit__方法如下所述.关于它们的一个非常好的事情是,__exit__即使发生异常也会调用它,因此您可以优雅地计算或关闭资源.

您的代码,增强了__enter__/ __exit__:

class fileHandler:
    def __init__(self, dbf):
        self.logger = logging.getLogger('fileHandler')
        self.thefilename = dbf
    def __enter__(self):
        self.thefile = open(self.thefilename, 'rb')
        return self
    def __exit__(self, *args):
        self.thefile.close()
Run Code Online (Sandbox Code Playgroud)

请注意,正在打开文件__enter__而不是__init__- 这允许您创建一次文件处理程序对象,然后在with不需要重新创建它时随时使用它:

fh = filehandler('some_dbf')
with fh:
    #file is now opened
    #do some stuff
#file is now closed
#blah blah
#need the file again, so
with fh:
    # file is open again, do some stuff with it
#etc, etc 
Run Code Online (Sandbox Code Playgroud)


Dun*_*can 6

正如您所写的那样,该类不会使文件更可靠地关闭.如果你简单地将文件处理程序实例放在地板上,那么在对象被销毁之前文件将不会关闭.这可能是直接或者可能不是,直到对象被垃圾收集,但只是将普通文件对象放在地板上就会快速关闭它.如果唯一的引用thefile是来自你的类对象内部,那么当filehandler垃圾收集thefile时也会被垃圾收集,因此同时关闭.

使用文件的正确方法是使用以下with语句:

with open(dbf, 'rb') as thefile:
    do_something_with(thefile)
Run Code Online (Sandbox Code Playgroud)

这将保证thefilewith子句退出时始终关闭.如果要将文件包装在另一个对象中,也可以通过定义__enter____exit__方法来完成:

class FileHandler:
    def __init__(self, dbf):
        self.logger = logging.getLogger('fileHandler')
        self.thefile = open(dbf, 'rb')
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        self.thefile.close()
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

with FileHandler(dbf) as fh:
    do_something_with(fh)
Run Code Online (Sandbox Code Playgroud)

并确保文件立即关闭.