适当处理文件的方式?

8 浏览
0 Comments

适当处理文件的方式?

我正在阅读Zed Shaw关于Python的书籍。

书中有一个练习要求将一个文件复制到另一个文件中,要求使用最少的行数来实现。我用一行代码完成了,代码如下:

(open(argv[2], 'w')).write(open(argv[1]).read())

我的问题是,如果不关闭文件,会发生什么最糟糕的情况呢?

我用另一种方法也做了同样的事情,教程说这种方法更可靠。你认为呢?

with open(argv[1], 'r') as source:
   with open(argv[2], 'w') as dest:
       dest.write(source.read())

我是一个新手,这样的问题可能看起来很傻,但对我来说很重要。

谢谢您的关注。Nick

0
0 Comments

在Python的大多数当前实现中,实际上,文件在文件对象取消引用时立即关闭,因为垃圾回收主要是通过引用计数完成的。然而,Python语言并不保证这一点:你正在依赖于一种实现细节。因此,这个一行代码的方法在当前或未来使用更先进的垃圾回收技术的Python实现中会失效(例如将GC委托给底层的JVM或.Net运行时)。使用with保证在块退出时立即关闭文件--在语言的任何正确实现中都是如此。因此,它肯定更健壮和防错。此外,假设由argv[2]指定的文件存在,但由arg[1]指定的文件不存在。在这个一行代码中:(open(argv[2], 'w')).write(open(argv[1]).read()),你首先打开要写入的文件(从而擦除其内容)--虽然有多余的括号,但它们是无害的:-)。然后你试图打开要读取的文件,得到一个异常并失败--但你打开的第一个文件无法恢复(即,在磁盘上保持为空)。在这种情况下,这不太可能是期望的行为。在with版本中,你首先尝试打开要读取的文件--如果失败,你永远不会擦除要写入的文件。对我来说,这也感觉更健壮--而且这适用于Python的任何版本,过去、现在或未来。还有一件事:我想知道规范是否断言文件内容适合内存。如果不适合,尝试一次性读取文件将导致内存错误--相反,你应该使用循环每次读取和写入一些较大但有界的BUFFER_SIZE字节。这强调了为了复制目的最好以二进制而不是文本模式打开文件(文本模式是默认模式,所以你的代码中使用了它,因为你没有指定其他模式)。在这方面,更健壮的方法是:

with open(argv[1], 'rb') as source:
    with open(argv[2], 'wb') as dest:
        while True:
            buf = source.read(BUFFER_SIZE)
            if not buf: break
            dest.write(buf)

。有趣的是,即使在最简单的任务中,也会出现许多小细节的问题,不是吗?这就是为什么学习Python的大型标准库是关键,它充满了小心编码的模块,专门处理可能出现的各种边界情况。真正回答这个问题的答案(如果我在面试声称掌握Python的求职者那里)是(鼓掌声...):

import shutil
shutil.copy2(argv[1], argv[2])

!请参阅https://docs.python.org/2/library/shutil.html#shutil.copy2中的文档(以及同一模块中的copycopystat函数上方):它安全地和可靠地复制“权限位、最后访问时间、最后修改时间和标志”……文件复制比眼睛看到的更复杂,而shutil会替你处理所有这些。学习Python标准库至少与学习语言本身一样重要!例如,PyPy并不会自动关闭文件,因为它对所有东西都使用垃圾回收。我曾在一个非常流行的库上报告了一个bug,它在PyPy上崩溃,因为它在一个紧密循环中执行open(...).read()并耗尽了文件描述符!非常好的完整解释。非常感谢。

0