【问题标题】:How does open handle context management?open 如何处理上下文管理?
【发布时间】:2018-08-13 19:07:41
【问题描述】:

python 内置插件 openfile 以我不太了解的方式与上下文管理器一起工作。

据我了解,open 将创建一个filefile 实现了上下文管理方法 __enter____exit__。我最初希望__enter__ 实现文件描述符的实际打开。

但是,在 with 块之外使用 open 将返回一个已打开的 file。因此,似乎file.__init__open 实际上正在打开文件描述符,据我所知file.__enter__ 没有做任何事情。或者file.__init__/open 直接调用file.__enter__

第一个问题:

open 内置函数的执行流程是什么? open 处理什么,file.__init__ 处理什么,file.__enter__ 处理什么?当重复使用一个file 对象来打开/关闭文件的多个循环时,这是如何工作的?这与为多个上下文循环重用其他上下文管理器对象有何不同?

第二个问题:

file 等对象具有设置步骤和拆卸步骤。设置发生在 __init__ 中,而拆除发生在 close__exit__ 中。

这是一个好的设计模式吗?是否应该为自定义函数/上下文管理器实现此设计模式?

【问题讨论】:

  • Python 是开源的,这意味着您可以通过查看它here 来了解它是如何实现的,并回答您自己的问题。
  • 我觉得github.com/python/cpython现在是首选

标签: python file with-statement contextmanager


【解决方案1】:

如果您查看 _pyio.py(io 模块的纯 Python 实现),您会在 IOBase 类中找到以下代码:

### Context manager ###

def __enter__(self):  # That's a forward reference
    """Context management protocol.  Returns self (an instance of IOBase)."""
    self._checkClosed()
    return self

def __exit__(self, *args):
    """Context management protocol.  Calls close()"""
    self.close()

这包含您大部分问题的答案。要理解的重要一点是上下文管理器的功能是确保在完成文件后关闭文件。它只需调用close 函数就可以做到这一点,这样就省去了这样做的麻烦。

file.__enter__ 处理什么?没有。它只是将作为调用内置函数 open() 的结果的文件对象返回给您。

当使用一个文件对象打开和关闭文件的多个循环时,这是如何工作的?上下文管理器对于该目的不是很有用,因为您必须每次都显式打开文件。

这是一个好的设计模式吗?是的,因为它减少了您必须编写的代码量,所以很容易阅读和理解。

是否应该为自定义函数/上下文管理器实现此模式?任何时候你有一个需要清理的对象,或者有涉及某种类型的打开/关闭概念的使用,你都应该考虑这种模式。标准库还有很多其他的例子。

【讨论】:

    【解决方案2】:

    对于问题 1

    在 CPython 中,open() 只创建一个 file 对象,其底层 C 类型为 PyFileObject;参见bltinmodule.cfileobject.c中的源代码

    static PyObject *
    builtin_open(PyObject *self, PyObject *args, PyObject *kwds)
    {
        return PyObject_Call((PyObject*)&PyFile_Type, args, kwds);
    }
    
    • file.__init__ 会打开文件

    • file.__enter__ 确实什么都不做,除了对字段 file.fp 进行空检查

    • file.__exit__调用close()方法关闭文件

    关于问题 2

    为什么file会这样设计是有历史原因的。

    openwith 是在不同版本的 CPython 上引入的两个不同的关键字。 with 被引入到 Python 2.5(参见 PEP 343)。那时候open已经用了很久了。

    对于我们的自定义类型,我们可以像 file 那样设计,取决于具体的应用程序上下文。

    比如threading.Lock是不同的设计,它的initenter是分开的。

    【讨论】:

      猜你喜欢
      • 2021-07-09
      • 2020-06-07
      • 1970-01-01
      • 2016-05-30
      • 2015-03-25
      • 2023-03-07
      • 2017-10-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多