15

I've got a class that wraps some file handling functionality I need. Another class creates an instance of the filehandler and uses it for an indeterminate amount of time. Eventually, the caller is destroyed, which destroys the only reference to the filehandler.

What is the best way to have the filehandler close the file?

I currently use __del__(self) but after seeing several different questions and articles, I'm under the impression this is considered a bad thing.

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

That's the relevent bit of the handler. The whole point of the class is to abstract away details of working with the underlying file object, and also to avoid reading the entire file into memory unnecessarily. However, part of handling the underlying file is closing it when the object falls out of scope.

The caller is not supposed to know or care about the details involved in the filehandler. It is the filehandler's job to release any necessary resources involved when it falls out of scope. That's one of the reasons it was abstracted in the first place. So, I seem to be faced with moving the filehandler code into the calling object, or dealing with a leaky abstraction.

Thoughts?

4

2 回答 2

23

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

所以,__del__没关系,只是要警惕循环引用。

垃圾回收:这里的重点是,当一个对象超出范围时,它可以被垃圾回收,实际上,它被垃圾回收......但是什么时候?无法保证何时发生,不同的 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()

请注意,正在打开文件__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 
于 2011-12-20T22:29:59.143 回答
8

正如您编写的那样,该类不会使文件更可靠地关闭。如果您简单地将文件处理程序实例放在地板上,那么文件在对象被销毁之前不会关闭。这可能会立即发生,也可能不会直到对象被垃圾收集,但只需将一个普通文件对象放在地板上就会很快关闭它。如果唯一的引用thefile来自您的类对象内部,那么filehandler垃圾收集thefile的时间也将被垃圾收集并因此同时关闭。

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

with open(dbf, 'rb') as thefile:
    do_something_with(thefile)

这将保证在子句退出thefile时始终关闭。with如果您想将文件包装在另一个对象中,您也可以通过定义__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()

然后你可以这样做:

with FileHandler(dbf) as fh:
    do_something_with(fh)

并确保文件被及时关闭。

于 2011-12-20T21:47:44.903 回答