作为线程如何附加到文件?,大多数答案是关于打开一个文件并附加到它,例如:
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
语句什么都不做…
让我们忽略所有这些,只讨论一遍又一遍地打开和关闭文件的优点和缺点:
- 浪费一点时间。如果你真的很不走运,文件甚至可能会一直从磁盘缓存中掉出来,浪费更多的时间
- 确保您总是附加到文件的末尾,即使其他程序也附加到同一个文件。(这对于例如syslog类型的日志非常重要。)1
- 确保您已在某个时刻将写操作刷新到磁盘,这样可以减少程序崩溃或终止时数据丢失的几率
- 确保写入后立即将写入刷新到磁盘。如果你试图在同一程序或不同程序的其他地方打开和读取文件,或者最终用户只是在记事本中打开它,你不会错过最后1.73KB的行,因为它们仍然在某个缓冲区中,直到稍后才会写入2
所以,这是一种权衡。通常,您想要其中一种保证,而性能成本并不是什么大不了的事。有时候,这是一件大事,保证并不重要。有时,你真的需要两者,所以你必须写一些复杂的东西,手动缓冲比特,一次写入并刷新所有比特。
1。正如open
的Python文档所表明的,这无论如何都会在一些Unix系统上发生。但不是在其他Unix系统上,也不是在Windows上
2.此外,如果有多个写入程序,它们一次都会追加一行,而不是在每次刷新时都追加,这对日志文件来说同样非常重要
如果可能,一般应避免global
。
人们在处理文件时使用with
命令的原因是它明确控制了作用域。一旦with
运算符完成,则关闭文件并丢弃文件变量。
您可以避免使用with
运算符,但必须记住调用myfile.close()
。尤其是在处理大量文件的情况下。
避免使用with
块的一种方法也是避免使用全局的
def filesave(f_obj, string):
f_obj.write(string)
f = open(filename, 'a')
filesave(f, "test1n")
filesave(f, "test2n")
f.close()
然而,在这一点上,你最好去掉这个功能,简单地做:
f = open(filename, 'a')
f.write("test1n")
f.write("test2n")
f.close()
在这一点上,你可以很容易地将它放在with
块中:
with open(filename, 'a') as f:
f.write("test1n")
f.write("test2n")
是的。没有任何理由不去做你正在做的事情。这不太像Python。
后一种代码可能更高效,但前一种代码更安全,因为它确保每次对FileSave
的调用写入文件的内容都被刷新到文件系统,以便其他进程可以读取更新的内容,并通过使用open
作为上下文管理器关闭每次调用的文件句柄,您允许其他进程也有机会写入该文件(特别是在Windows中)。
这确实取决于具体情况,但这里有一些想法:
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
,可以避免手动花费换行符。