【问题标题】:does close() imply flush() in Python?close() 在 Python 中是否暗示 flush()?
【发布时间】:2011-01-27 16:13:57
【问题描述】:

在 Python 中,一般情况下 - 对文件对象的 close() 操作是否意味着 flush() 操作?

【问题讨论】:

    标签: python operating-system flush


    【解决方案1】:

    作为对这个问题的补充,是的,python 在关闭之前刷新,但是如果您想确保数据正确写入磁盘,这还不够。

    这就是我编写文件的方式,无论目标文件是否存在,它都会在 UNIX/Linux 服务器上自动更新。请注意,某些文件系统会在关闭+重命名时将数据隐式提交到磁盘(ext3data=ordered(默认),并且 ext4 在添加写入检测之前最初发现了许多应用程序缺陷-关闭重命名模式并在元数据之前同步数据[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(...),即使写入路径中有不安全的缓存,您仍然可能会丢失数据。这在消费级硬件上很常见,尽管任何系统都可以配置为不安全的写入,这也提高了性能。至少即使使用不安全的缓存,上述方法仍应保证您获得的任何数据副本都是有效的。

    【讨论】:

      【解决方案2】:

      是的,在 Python 3 中这最终是 in the official documentation,但在 Python 2 中已经是这种情况(参见 Martin's answer)。

      【讨论】:

        【解决方案3】:

        是的。它使用底层的 close() 函数为您执行此操作 (source)。

        【讨论】:

        • (换句话说:缓冲的文件 I/O 是大的抽象和隐藏的远离你。做一个openwriteclose 不应该留下不写的东西,因为那是你对write 的意图。一个经常吃掉扔给它的东西的缓冲区将是一个非常糟糕的设计[或一个饥饿的缓冲区]。)
        • 谢谢,这也是我的猜测。但这是真正的跨平台、跨操作系统、跨语言吗?
        • @Adam Matan:这就是 Python 位于 C 库之上的原因。保证“这个真正的跨平台、跨操作系统”。我不知道“跨语言”是什么意思。
        • +1 谢谢。我所说的“跨语言”是想问这种行为在绝大多数现代编程语言中是否相似。
        • “是的。它使用底层的 close() 函数为你做这件事……” -> 我认为你应该再次阅读man 2 close(),因为这是不正确
        【解决方案4】:

        filehandle.close 不一定要刷新。令人惊讶的是, filehandle.flush 也无济于事——当 Python 运行时,它仍然会卡在 OS 缓冲区中。观察我写入文件、关闭文件并在 shell 命令提示符下按 Ctrl-Z 并检查文件的会话:

        $  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 包中获得)应该将所有缓冲区刷新到磁盘,但它可能会对系统范围的性能产生影响(它会同步系统上的所有文件)。

        【讨论】:

        • 嗯 - 我怀疑你的问题是你实际上并没有打电话 flush()close() - 你只是展示了他们的代表!你需要括号来调用这些方法。
        【解决方案5】:

        注意:close()flush() 不能确保磁盘上的数据实际上是安全的。它只是确保操作系统有数据==它没有在进程中缓冲。

        您可以尝试同步或 fsync 将数据写入磁盘。

        【讨论】:

        • 是的,但现代操作系统不会在进程终止时将数据写入磁盘吗?
        • 取决于您所谈论的时间尺度。例如某些版本的 ext4 可能会等待整整几秒钟才能将数据提交到光盘。
        • +1 如果数量级是秒,我就很安全了。谢谢!
        猜你喜欢
        • 2015-10-06
        • 1970-01-01
        • 2012-04-09
        • 2011-03-17
        • 2023-03-18
        • 2016-04-05
        • 2018-03-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多