在异常退出期间是否关闭文件?

2024-04-20 14:23:28 发布

您现在位置:Python中文网/ 问答频道 /正文


Tags: python
3条回答

不,他们没有

如果希望关闭文件(即使发生异常),请使用with语句。

docs

The with statement is used to wrap the execution of a block with methods defined by a context manager. This allows common try...except...finally usage patterns to be encapsulated for convenient reuse.

来自docs

with语句允许像文件这样的对象以某种方式使用,以确保它们总是被及时、正确地清理掉。

with open("myfile.txt") as f:
    for line in f:
        print line,

执行语句后,文件f始终关闭,即使在处理行时遇到问题。提供预定义清理操作的其他对象将在其文档中指明这一点。

一个相当直截了当的问题。

两个答案。

一个说,“是的。”

另一个说,“不!“。”

两人都获得了重要的支持票。

相信谁?让我来澄清一下。


两个答案都有一定的道理,这取决于你所说的 正在关闭文件。

首先,考虑从操作系统的 观点。

当进程退出时,操作系统clears up all the resources that only that process had open。否则表现不好的程序 崩溃但没有释放资源会消耗整个系统 资源。

如果Python是唯一打开该文件的进程,那么该文件将 闭嘴。类似地,操作系统将清除 进程、任何仍然打开的网络端口,以及其他 东西。有一些特殊的函数,比如^{},可以创建 对象,但在大多数情况下 操作系统负责一切。

现在,从Python的角度关闭文件怎么样?如果有程序 使用任何编程语言编写的出口,大多数资源都将被清除 但是Python如何在标准Python程序中处理清理呢?

与其他Python不同,Python的标准CPython实现 像Jython这样的实现使用引用计数来完成 垃圾收集。对象具有引用计数字段。每一次 Python中的某个对象获取对另一个对象的引用 引用的对象中的count字段将递增。当引用是 丢失,例如,由于变量不再在范围内,引用计数为 递减。当引用计数为零时,没有Python代码可以达到 对象不再存在,因此对象被释放。当它变得 解除分配,Python calls the ^{} destructor

Python文件的__del__()方法刷新缓冲区并关闭 从操作系统的角度来看。因为参考 在CPython中,如果在函数中打开一个文件,并且不返回 文件对象,则当 函数退出,文件将自动刷新并关闭。什么时候? 程序结束,CPython取消对所有对象的引用,所有对象都有 它们的析构函数被调用,即使程序由于未被破坏而结束 例外。(从技术上讲,对于病理性病例,如果你有cycle of objects with destructors, 至少在Python版本中before 3.4。)

但这只是CPython实现。Python语言的定义 在Python language reference中,这是所有Python 为了调用它们自己,需要遵循实现 与Python兼容。

语言引用在其data model section中解释了资源管理:

Some objects contain references to “external” resources such as open files or windows. It is understood that these resources are freed when the object is garbage-collected, but since garbage collection is not guaranteed to happen, such objects also provide an explicit way to release the external resource, usually a close() method. Programs are strongly recommended to explicitly close such objects. The ‘try...finally‘ statement and the ‘with‘ statement provide convenient ways to do this.

也就是说,CPython通常会立即关闭对象,但是 未来版本中的更改,其他Python实现甚至 需要完全关闭对象。

所以,对于可移植性和explicit is better than implicit, 强烈建议对所有可能 close()d,如果在 对象创建和可能引发异常的close()。或使用 完成相同任务的with语法糖。如果你这样做了 然后,文件上的缓冲区将被刷新,即使异常是 提高。

然而,即使使用with语句,相同的底层机制也是 在工作中。如果程序崩溃的方式没有给出Python的 __del__()方法如果有机会运行,仍然可能导致损坏 磁盘上的文件:

#!/usr/bin/env python3.3

import ctypes

# Cast the memory adress 0x0001 to the C function int f()
prototype = ctypes.CFUNCTYPE(int)
f = prototype(1)

with open('foo.txt', 'w'):
    x.write('hi')
    # Segfault
    print(f())

这个程序产生一个零长度的文件。这是个不正常的案子,但是 表明即使使用with语句资源也不会总是 一定要按你期望的方式清理干净。Python告诉操作人员 系统打开要写入的文件,该文件在磁盘上创建;Python写入hi 进入C库的stdio缓冲区;以及它在with之前崩溃 语句结束,由于明显的内存损坏,它不安全 使操作系统尝试读取缓冲区的剩余部分并将其刷新到磁盘。因此,即使存在with语句,程序也无法正确清理。哇哦。尽管如此,close()with几乎总是有效的,并且你的程序总是比没有它们好。

所以答案既不是“是”也不是“否” 对于大多数普通的CPython程序来说是必需的。但不使用它们会导致 不可移植的代码,这将是错误的。当它们是极端的时 有帮助的是,在病理病例中他们仍然有可能失败。

是的。

这是一个CLIB(至少在cpython中)和操作系统。当脚本退出时,CLIB将刷新并关闭所有文件对象。即使没有(例如python本身崩溃),操作系统也会像其他进程一样关闭其资源。不管它是一个异常还是一个正常的出口,或者它的python或任何其他程序。

下面是一个脚本,它在文件内容刷新到磁盘之前写入文件并引发异常。工作正常:

~/tmp/so$ cat xyz.txt
cat: xyz.txt: No such file or directory
~/tmp/so$ cat exits.py
f = open("xyz.txt", "w")
f.write("hello")
print("file is", open("xyz.txt").read())
assert False

~/tmp/so$ python exits.py
('file is', '')
Traceback (most recent call last):
  File "exits.py", line 4, in <module>
    assert False
AssertionError
~/tmp/so$ cat xyz.txt
hello

相关问题 更多 >