是否需要closures没有参考的文件?

作为一个完整的编程初学者,我想了解打开和closures文件的基本概念。 我正在做的一个练习是创build一个脚本,允许我将内容从一个文件复制到另一个文件。

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() 

我试图缩短这个代码,并提出这个:

 indata = open(from_file).read() open(to_file, 'w').write(indata) 

这工作,看起来更有效率,我。 但是,这也是我感到困惑的地方。 我想我省略了对打开的文件的引用; 不需要in_file和out_filevariables。 但是,这是否使我有两个文件是开放的,但没有提到他们? 我如何closures这些,或者没有必要?

任何有关这个话题的帮助,都非常感谢。

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

作为一个操作系统资源, 一个进程可以保持打开的文件数量是有限的:很久以前,在Unix上每个进程的限制大约是20个。 现在我的OS X盒子限制了256个打开的文件(尽pipe这是一个强加的限制,可以提高)。 其他系统可能会限制几千 ,或成千上万 (每个用户,而不是每个进程在这种情况下)的限制。 当程序结束时,所有资源都会自动释放。 所以如果你的程序打开几个文件,做一些事情并退出,你可能会马虎,你永远不会知道它们的差异。 但是,如果您的程序将打开数千个文件,那么您将很好地释放打开的文件以避免超出操作系统限制。

在进程退出之前closures文件还有另外一个好处:如果你打开一个文件进行写操作, closures文件将首先“刷新它的输出缓冲区”。 这意味着I / O库通过收集(“缓冲”)您写出的内容并批量保存到磁盘来优化磁盘使用。 如果您将文本写入文件并立即尝试重新打开并在不首先closures输出句柄的情况下读取它,则会发现并非所有内容都已写出。 另外,如果你的程序太突然(有信号,偶尔甚至通过正常退出)closures,输出可能永远不会被刷新。

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

  1. close()显式地表示。 (注意python新手:不要忘了这些parens!我的学生们喜欢写in_file.close ,它什么都不做。)

  2. build议:隐式地用with语句打开文件。 即使在exception终止(来自exception)的情况下,在达到with块的末尾时,将调用close()方法。

     with open("data.txt") as in_file: data = in_file.read() 
  3. 由引用pipe理器或垃圾收集器隐含,如果您的python引擎实现它。 这是不推荐的,因为它不是完全可移植的; 查看其他答案的细节。 这就是为什么with语句被添加到python。

  4. 隐含的,当你的程序结束。 如果文件打开输出,这可能会导致程序在所有内容刷新到磁盘之前退出。

处理这个问题的pythonic方法是使用with context manager :

 with open(from_file) as in_file, open(to_file, 'w') as out_file: indata = in_file.read() out_file.write(indata) 

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

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

在你的情况下,这样做

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

将为to_file创build一个文件对象,但不会将其命名为一个名称 – 这意味着没有对它的引用。 这行之后你不可能操纵对象。

CPython会检测到这个,并在使用完之后清理对象。 在文件的情况下,这意味着自动closures它。 原则上,这是好的,你的程序不会泄漏内存。

这个“问题”是这个机制是CPython解释器的一个实现细节。 语言标准明确无法保证! 如果您正在使用诸如pypy之类的替代解释器,则文件的自动closures可能会无限期地延迟。 这包括其他隐式操作,如closures时刷新写入。

这个问题也适用于其他资源,例如networking套接字。 总是明确地处理这样的外部资源是一个好习惯。 从Python 2.6开始, with语句使得这个优雅:

 with open(to_file, 'w') as out_file: out_file.write(in_data) 

TLDR:有用,但请不要这样做。

到目前为止的答案是绝对正确的, 当在python中工作 。 你应该使用with open()上下文pipe理器。 这是一个很棒的内置function,可以帮助您快速完成常见的编程任务(打开和closures文件)。

但是,由于您是初学者,并且无法访问上下文pipe理器和整个职业生涯中的自动引用计数 ,所以我将从一般编程的angular度来解决这个问题。

你的代码的第一个版本非常好。 您打开一个文件,保存参考,从文件中读取,然后closures它。 当语言不提供任务的快捷方式时,这是如何编写大量的代码。 我唯一能改进的地方就是把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() 

处理文件对象时,最好使用with关键字。 这有一个好处,就是文件在套件结束后可以正常closures,即使在路上出现exception。 它也比编写等价的try-finally块要短得多:

 >>> with open('workfile', 'r') as f: ... read_data = f.read() >>> f.closed True 

一个安全的方式来打开文件,而不必担心你没有closures它们是这样的:

 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)