Python文件对象,结束和析构函数

时间:2013-04-10 05:17:40

标签: python file-io destructor

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__()方法 调用解释器退出时仍然存在的对象。“所以 到目前为止,一切都与文档一致。

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

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

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

编辑:请注意,我不是在讨论文件 descriptors (由操作系统缓冲并在程序退出时由操作系统关闭),但关于Python文件可以实现自己缓冲的对象。

3 个答案:

答案 0 :(得分:14)

在Windows上,NamedTemporaryFile使用特定于Windows的扩展名(os.O_TEMPORARY)来确保文件在关闭时被删除。如果该过程以任何方式被杀死,这可能也有效。但是在POSIX上没有明显的等价物,很可能是因为在POSIX上你可以简单地删除仍在使用的文件;它只删除名称,文件的内容仅在关闭后以任何方式删除。但实际上假设我们希望文件名在文件关闭之前一直存在,就像使用NamedTemporaryFile一样,那么我们需要“魔术”。

我们不能使用与刷新缓冲文件相同的魔力。发生的事情是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)

答案 1 :(得分:1)

在* nix的任何版本上,当进程完成时,所有文件描述符都将关闭,这由操作系统负责。 Windows在这方面可能完全相同。如果不深入挖掘源代码,我不能100%权威地说实际发生了什么,但可能会发生的是:

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

  • 如果deleteTrue,则可能使用C函数remove()。这将在进程退出之前强行删除文件。

答案 2 :(得分:-1)

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