【问题标题】:Why it's needed to open file every time we want to append the file为什么每次我们要附加文件时都需要打开文件
【发布时间】:2018-09-10 03:29:39
【问题描述】:

作为How do you append to a file?的线程,大多数答案是打开一个文件并附加到它,例如:

def FileSave(content):
    with open(filename, "a") as myfile:
        myfile.write(content)

FileSave("test1 \n")
FileSave("test2 \n")

我们为什么不直接提取 myfile 并仅在调用 FileSave 时写入。

global myfile
myfile = open(filename)
def FileSave(content):
    myfile.write(content)

FileSave("test1 \n")
FileSave("test2 \n")

后一种代码是否更好,因为它只打开文件一次并多次写入? 或者,没有区别,因为 python 内部的内容将保证文件只打开一次,尽管open 方法被多次调用。

【问题讨论】:

  • 修改后的代码存在许多与您的问题无关的问题:您以只读模式打开文件,从不关闭文件,您有一个 global什么都不做的声明……

标签: python


【解决方案1】:

修改后的代码存在许多与您的问题无关的问题:您以只读模式打开文件,从不关闭文件,您有一个 global 语句什么都不做……

让我们忽略所有这些,只讨论一遍又一遍地打开和关闭文件的优缺点:

  • 浪费一点时间。如果你真的不走运,文件甚至可能只是勉强从磁盘缓存中掉出来,浪费更多时间。
  • 确保您始终追加到文件的末尾,即使某些其他程序也在追加到同一文件。 (这对于系统日志类型的日志等非常重要。)1
  • 确保您已在某个时间点将写入刷新到磁盘,从而在您的程序崩溃或被终止时降低数据丢失的可能性。
  • 确保您在写入后立即将写入刷新到磁盘。如果您尝试在同一程序或不同程序中的其他地方打开和读取文件,或者如果最终用户只是在记事本中打开它,您将不会丢失最后 1.73KB 的行,因为它们仍然在某处的缓冲区中,直到稍后才会写入。2

所以,这是一个权衡。通常,您需要这些保证之一,而性能成本并不是什么大问题。有时,这很重要,保证并不重要。有时,你真的需要两者,所以你必须编写一些复杂的东西,你手动缓冲位并一次写入并刷新它们。


1。正如open 的 Python 文档所明确指出的,这无论如何都会在一些 Unix 系统上发生。但不是在其他 Unix 系统上,也不是在 Windows 上。..

2。此外,如果您有多个写入器,它们都是一次追加一行,而不是在它们碰巧刷新时追加,这对于日志文件来说再次非常重要。

【讨论】:

  • 目前还不清楚您分配哪个版本的哪个(不利)优势。
  • @viraptor 这些都是反复打开和关闭文件的优缺点。
  • 我不确定第二点是否属实。多个进程可以在追加模式下打开同一个文件,并且无论它们最终写入的顺序如何,它们都将追加到末尾。
  • @viraptor 我真的不知道如何比字面上说“一遍又一遍地打开和关闭文件的优点和缺点:”更清楚。什么会让你更清楚这一点?
  • @viraptor No. 'a' 文件在某些​​ Unix 系统上总是写到最后,但在其他系统上则不然,POSIX 明确表示这是依赖于实现的,不应依赖。在 Windows 上并非如此。
【解决方案2】:

一般global 应该是avoided if possible

人们在处理文件时使用with 命令的原因是它显式地控制了范围。一旦with 操作符完成,文件将被关闭并且文件变量被丢弃。

您可以避免使用with 运算符,但您必须记住调用myfile.close()。特别是如果您要处理大量文件。

避免使用with 块的一种方法也避免使用全局是

 def filesave(f_obj, string):
     f_obj.write(string)

 f = open(filename, 'a')
 filesave(f, "test1\n")
 filesave(f, "test2\n")
 f.close()

但是此时你最好摆脱该功能而只是简单地做:

f = open(filename, 'a')
f.write("test1\n")
f.write("test2\n")
f.close()

此时您可以轻松地将其放入 with 块中:

with open(filename, 'a') as f:
    f.write("test1\n")
    f.write("test2\n")

所以是的。没有理由不去做你正在做的事情。它只是不是很 Pythonic。

【讨论】:

    【解决方案3】:

    后一种代码可能更高效,但前一种代码更安全,因为它确保每次调用 FileSave 写入文件的内容都会刷新到文件系统,以便其他进程可以读取更新的内容,并且通过使用 open 作为上下文管理器在每次调用时关闭文件句柄,您可以让其他进程也有机会写入文件(特别是在 Windows 中)。

    【讨论】:

    • 在您间歇性写入文件时允许其他程序打开文件是在 Windows 上使您的程序崩溃的好方法。
    【解决方案4】:

    这确实取决于具体情况,但这里有一些想法:

    with 块绝对保证一旦块退出,文件将被关闭。 Python 不会对附加文件进行奇怪的优化。

    一般而言,全局变量会使您的代码模块化程度降低,因此更难阅读和维护。你会认为原来的 FileSave 函数试图避免全局变量,但它使用的是全局名称 filename,所以此时你不妨完全使用全局文件,因为它会为你节省一些 I/O 开销.

    更好的选择是完全避免使用全局变量,或者至少正确使用它们。你真的不需要单独的函数来包装file.write,但如果它代表更复杂的东西,这里有一个设计建议:

    def save(file, content):
        print(content, file=file)
    
    def my_thing(filename):
        with open(filename, 'a') as f:
            # do some stuff
            save(f, 'test1')
            # do more stuff
            save(f, 'test2')
    
    if __name__ == '__main__':
        my_thing('myfile.txt')
    

    请注意,当您将模块作为脚本调用时,在全局范围内定义的文件名将被传递给主例程。但是,由于主例程不引用全局变量,因此您可以 A) 更容易阅读,因为它是自包含的,并且 B) 对其进行测试,而不必想知道如何在不破坏其他所有内容的情况下为其提供输入。

    另外,通过使用print 而不是file.write,您可以避免手动使用换行符。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-28
      • 1970-01-01
      • 1970-01-01
      • 2018-12-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多