在 Python 中,一般来说 -close()
对文件对象的操作是否意味着flush()
操作?
5 回答
是的。它使用为您执行此操作的底层close()
函数(source)。
注意:close()
并且flush()
不会确保数据在磁盘上实际上是安全的。它只是确保操作系统有数据==它没有在进程中缓冲。
您可以尝试同步或 fsync 以将数据写入磁盘。
是的,在 Python 3 中,这最终在官方文档中,但在 Python 2 中已经是这种情况(参见Martin 的回答)。
作为对这个问题的补充,是的,python 在关闭之前刷新,但是如果您想确保数据正确写入磁盘,这还不够。
这就是我编写文件的方式,无论目标文件是否存在,它都会在 UNIX/Linux 服务器上自动更新。请注意,某些文件系统将在 close+rename 时隐式地将数据提交到磁盘(ext3 with data=ordered
(默认),而ext4在添加对 write-close-rename 模式的检测之前发现了许多应用程序缺陷并在元数据之前同步数据[1])。
# Write destfile, using a temporary name .<name>_XXXXXXXX
base, name = os.path.split(destfile)
tmpname = os.path.join(base, '.{}_'.format(name)) # This is the tmpfile prefix
with tempfile.NamedTemporaryFile('w', prefix=tmpname, delete=False) as fd:
# Replace prefix with actual file path/name
tmpname = str(fd.name)
try:
# Write fd here... ex:
json.dumps({}, fd)
# We want to fdatasync before closing, so we need to flush before close anyway
fd.flush()
os.fdatasync(fd)
# Since we're using tmpfile, we need to also set the proper permissions
if os.path.exists(destfile):
# Copy destination file's mask
os.fchmod(fd.fileno, os.stat(destfile).st_mode)
else:
# Set mask based on current umask value
umask = os.umask(0o22)
os.umask(umask)
os.fchmod(fd.fileno, 0o666 & ~umask) # 0o777 for dirs and executable files
# Now we can close and rename the file (overwriting any existing one)
fd.close()
os.rename(tmpname, destfile)
except:
# On error, try to cleanup the temporary file
try:
os.unlink(tmpname)
except OSError:
pass
raise
恕我直言,如果 Python 提供了简单的方法来解决这个问题,那就太好了......同时我想如果你关心数据一致性,最好真正了解低级别发生的事情,特别是因为有很多差异各种操作系统和文件系统。
另请注意,这并不能保证可以恢复写入的数据,只保证您将获得一致的数据副本(旧的或新的)。为确保新数据在返回时被安全写入和访问,您需要os.fsync(...)
在重命名后使用,即使写入路径中有不安全的缓存,您仍然可能丢失数据。这在消费级硬件上很常见,尽管任何系统都可以配置为不安全的写入,这也提高了性能。至少即使使用不安全的缓存,上述方法仍应保证您获得的任何数据副本都是有效的。
filehandle.close 不一定要刷新。令人惊讶的是,filehandle.flush 也无济于事——当 Python 运行时,它仍然会卡在操作系统缓冲区中。观察这个会话,我在其中写入文件,将其关闭,然后按 Ctrl-Z 到 shell 命令提示符并检查文件:
$ cat xyz
ghi
$ fg
python
>>> x=open("xyz","a")
>>> x.write("morestuff\n")
>>> x.write("morestuff\n")
>>> x.write("morestuff\n")
>>> x.flush
<built-in method flush of file object at 0x7f58e0044660>
>>> x.close
<built-in method close of file object at 0x7f58e0044660>
>>>
[1]+ Stopped python
$ cat xyz
ghi
随后我可以重新打开文件,这必然会同步文件(因为在这种情况下,我以附加模式打开它)。正如其他人所说,同步系统调用(可从 os 包中获得)应该将所有缓冲区刷新到磁盘,但它可能会对系统范围的性能产生影响(它会同步系统上的所有文件)。