Python文件对象、关闭和析构函数



tempfile.NamedTemporaryFile()的描述为:

如果delete为true(默认值),则文件将被立即删除关闭。

在某些情况下,这意味着文件在执行命令后不会被删除Python解释器结束。例如,在下运行以下测试时py.test,临时文件保留:

from __future__ import division, print_function, absolute_import
import tempfile
import unittest2 as unittest
class cache_tests(unittest.TestCase):
    def setUp(self):
        self.dbfile = tempfile.NamedTemporaryFile()
    def test_get(self):
        self.assertEqual('foo', 'foo')

在某种程度上这是有意义的,因为这个程序从来没有显式地关闭文件对象。这是物体闭合的唯一方法应该是在__del__析构函数中,但这里的语言参考文献指出"不能保证__del__()方法是正确的为解释器退出时仍然存在的对象调用。"到目前为止,一切都与文档一致。

然而,我对这句话的含义感到困惑。如果不是的话保证文件对象在解释器退出时关闭,可以吗可能会发生一些数据被成功写入(缓冲的)文件对象丢失,即使程序正常退出,因为它还在文件对象的缓冲区中,而文件对象从来没有关闭过?

对我来说,这似乎是非常不可能和反python的,而open()文档也不包含任何此类警告。所以我(暂时地)得出结论,文件对象毕竟是可以保证的被关闭。

但是这个魔法是如何发生的,为什么NamedTemporaryFile()不能使用同样魔术确保文件被删除?

Edit:请注意,我在这里谈论的不是文件描述符(由操作系统缓冲并在程序退出时由操作系统关闭),而是可能实现自己缓冲的Python文件对象。

在Windows上,NamedTemporaryFile使用Windows特定的扩展名(os.O_TEMPORARY)来确保文件在关闭时被删除。如果进程以任何方式终止,这也可能有效。然而,在POSIX上没有明显的等效,很可能是因为在POSIX上您可以简单地删除仍在使用的文件;它只删除名称,并且文件的内容只在关闭后(以任何方式)删除。但实际上,假设我们希望文件名一直保存到文件关闭,就像NamedTemporaryFile一样,那么我们需要"magic"。

不能使用与刷新缓冲文件相同的魔法。这里发生的事情是C库处理它(在Python 2中):文件是C中的FILE对象,并且C保证它们在正常的程序退出时被刷新(但如果进程被杀死则不会刷新)。在Python 3中,有自定义的C代码来实现相同的效果。但它是特定于这个用例的,而不是任何直接可重用的。

这就是NamedTemporaryFile使用自定义__del__的原因。事实上,__del__不能保证在解释器退出时被调用。(我们可以通过引用NamedTemporaryFile实例的全局循环来证明这一点;或运行PyPy而不是CPython。)

作为旁注,NamedTemporaryFile可以实现得更健壮一点,例如,通过在atexit中注册自己来确保文件名被删除。但是您也可以自己调用它:如果您的进程没有使用无限数量的NamedTemporaryFiles,则只需使用atexit.register(my_named_temporary_file.close)

在任何版本的*nix上,当一个进程结束时,所有的文件描述符都关闭,这是由操作系统处理的。Windows在这方面可能完全相同。在不深入研究源代码的情况下,我不能百分之百地说实际发生了什么,但可能发生的事情是:

  • 如果deleteFalse,则调用unlink()(或其他操作系统上类似的函数)。这意味着当进程退出并且没有更多打开的文件描述符时,该文件将被自动删除

  • 如果deleteTrue,很可能使用了C函数remove()

文件缓冲由操作系统处理。如果您在打开文件后没有关闭它,这是因为您假设操作系统将在所有者存在后刷新缓冲区并关闭该文件。这不是Python的魔法,这是你的操作系统在做它的事情。__del__()方法与Python相关,需要显式调用。

最新更新