是否需要关闭没有引用它们的文件?

Roy*_*ard 52 python file python-2.7

作为一个完整的编程初学者,我试图理解打开和关闭文件的基本概念.我正在做的一个练习是创建一个脚本,允许我将内容从一个文件复制到另一个文件.

in_file = open(from_file)
indata = in_file.read()

out_file = open(to_file, 'w')
out_file.write(indata)

out_file.close()
in_file.close()
Run Code Online (Sandbox Code Playgroud)

我试图缩短这段代码,并想出了这个:

indata = open(from_file).read()
open(to_file, 'w').write(indata)
Run Code Online (Sandbox Code Playgroud)

这对我来说效果更好一点.然而,这也是我感到困惑的地方.我想我遗漏了对打开文件的引用; 不需要in_file和out_file变量.但是,这是否会让我留下两个打开的文件,但没有任何文件可以引用它们?我如何关闭这些,或者没有必要?

非常感谢任何有助于阐明这一主题的帮助.

Roa*_*ich 57

处理此问题的pythonic方法是使用with上下文管理器:

with open(from_file) as in_file, open(to_file, 'w') as out_file:
    indata = in_file.read()
    out_file.write(indata)
Run Code Online (Sandbox Code Playgroud)

与这样的文件一起使用,with将确保为您完成所有必要的清理,即使read()write()抛出错误.

  • 注意:在CPython引用解释器中,`open`ing并立即`read`ing /`write`-ing到返回的文件对象(不存储对打开文件对象的引用)恰好是可预测和安全的,但那是大多数其他口译员的情况并非如此,并且它不是语言的合同保证(只是CPython引用计数的副作用).使用`with`更具可扩展性,可移植性和可预测性,所以总是使用它,即使看起来没有它也能正常工作. (25认同)
  • @RussellBorogove:可能是所有大的; 我不知道任何进行引用计数的非CPython实现.快速搜索显示[官方PyPy文档](http://pypy.org/compat.html)警告:open("filename","w").write("stuff")`只会写入文件一次GC运行一个集合,这是一个非常类似的情况.(如果"不可靠",你的意思是"可能会崩溃"或"可能会给出错误的结果",而不是"可能泄漏文件句柄",`open(foo).read()`不再容易比那个用open(foo)作为f:contents = f.read()`.) (8认同)
  • @RussellBorogove`open(foo).read()`应该是'safe',因为在执行`read()`期间,文件对象仍然会被引用.但是,当您有多个文件操作时,无法确保何时(或以何种顺序)关闭将发生,只是它将"在文件对象未被引用后的某个时间".这意味着写入可以被任意延迟(或者如果在程序退出时仍然引用的对象没有运行终结器,甚至可能甚至丢失,甚至CPython绝对不能保证:https://docs.python.org/2/reference/datamodel html的#对象.__ del__) (3认同)

ale*_*xis 38

您询问了"基本概念",所以让我们从顶部开始:当您打开文件时,您的程序可以访问系统资源,即访问程序自身内存空间之外的内容.这基本上是操作系统提供的一些魔力(系统调用,在Unix术语中).隐藏在文件对象内部是对"文件描述符"的引用,"文件描述符"是与打开文件关联的实际OS资源.关闭文件会告诉系统释放此资源.

作为OS资源,进程可以保持打开的文件数量是有限的:很久以前Unix上的每进程限制大约为20.现在我的OS X盒子限制了256个打开的文件(虽然这是一个强制限制,可以提出).其他系统可能会设置几千个限制,或者数万个(每个用户,在这种情况下不是每个进程).程序结束后,所有资源都会自动释放.因此,如果您的程序打开一些文件,对它们执行某些操作并退出,您可能会马虎虎虎,您永远不会知道其中的区别.但是,如果您的程序将打开数千个文件,那么您最好释放打开的文件以避免超出操作系统限制.

在进程退出之前关闭文件还有另一个好处:如果打开文件进行写入,关闭它将首先"刷新其输出缓冲区".这意味着i/o库通过收集("缓冲")您写出的内容并批量保存到磁盘来优化磁盘使用.如果您将文本写入文件并立即尝试重新打开并阅读它而不先关闭输出句柄,您会发现并非所有内容都已写出来.此外,如果您的程序过于突然关闭(有信号,或偶尔甚至正常退出),输出可能永远不会被刷新.

关于如何发布文件已经有很多其他答案,所以这里只是一个简短的方法列表:

  1. 明确地与close().(注意python新手:不要忘记parens!我的学生喜欢写in_file.close,什么都不做.)

  2. 推荐:隐含地,通过使用with语句打开文件.即使在异常终止(来自异常)的情况下,也会在到达块close()结束时调用该方法with.

    with open("data.txt") as in_file:
        data = in_file.read()
    
    Run Code Online (Sandbox Code Playgroud)
  3. 如果您的python引擎实现它,隐式地由引用管理器或垃圾收集器.不建议这样做,因为它不是完全便携的; 请参阅其他答案了解详情.这就是为什么with声明被添加到python.

  4. 当你的程序结束时,隐含地.如果文件打开以进行输出,则在将所有内容刷新到磁盘之前,可能存在程序退出的风险.


Mis*_*agi 36

默认的python interpeter CPython使用引用计数.这意味着一旦没有对象的引用,它就会被垃圾收集,即清理.

在你的情况下,做

open(to_file, 'w').write(indata)
Run Code Online (Sandbox Code Playgroud)

将创建一个文件对象to_file,但不将其设置为名称 - 这意味着没有对它的引用.你不可能在这一行之后操纵对象.

CPython将检测到这一点,并在使用后清理对象.对于文件,这意味着自动关闭它.原则上,这很好,你的程序不会泄漏内存.

"问题"是这个机制是CPython解释器的实现细节.语言标准明确无法保证!如果您使用替代解释器(如pypy),文件的自动关闭可能会无限期延迟.这包括其他隐式操作,例如在关闭时刷新写入.

此问题也适用于其他资源,例如网络套接字.始终明确处理此类外部资源是一种好习惯.从python 2.6开始,该with语句使这个优雅:

with open(to_file, 'w') as out_file:
    out_file.write(in_data)
Run Code Online (Sandbox Code Playgroud)

TLDR:它有效,但请不要这样做.

  • 不只是_closing_ - _writing_!在文件关闭之前,缓冲区不会被刷新... (6认同)

pyt*_*hon 7

with在处理文件对象时,最好使用关键字.这样做的好处是文件在套件完成后正确关闭,即使在途中引发了异常.它也比编写等效的try-finally块短得多:

>>> with open('workfile', 'r') as f:
...     read_data = f.read()
>>> f.closed
True
Run Code Online (Sandbox Code Playgroud)


Dar*_*ehe 7

到目前为止,在python中工作时,答案是完全正确的.您应该使用with open()上下文管理器.它是一个很棒的内置功能,可以帮助您快速完成常见的编程任务(打开和关闭文件).

但是,由于您是初学者,并且无法访问上下文管理器 以及整个职业生涯的自动引用计数,因此我将从一般的编程立场来解决这个问题.

您的代码的第一个版本非常好.您打开文件,保存引用,从文件中读取,然后关闭它.当语言没有为任务提供快捷方式时,这就是编写大量代码的方式.我唯一要改进的就是搬到close()你打开的地方并阅读文件.打开并读取文件后,您将内容存储在内存中,不再需要打开该文件.

in_file = open(from_file)
indata = in_file.read()
out_file.close() 

out_file = open(to_file, 'w')
out_file.write(indata)
in_file.close()
Run Code Online (Sandbox Code Playgroud)


Mar*_*ton 5

打开文件的安全方法,而不必担心你没有关闭它们是这样的:

with open(from_file, 'r') as in_file:
    in_data = in_file.read()

with open(to_file, 'w') as out_file:
    outfile.write(in_data)
Run Code Online (Sandbox Code Playgroud)