open 如何处理上下文管理?

How does open handle context management?

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

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

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

第一个问题:

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

第二个问题:

file这样的对象有设置步骤和拆卸步骤。设置发生在 __init__ 中,拆卸发生在 close__exit__.

这是一个好的设计模式吗?是否应为自定义 functions/context 经理实施此设计模式?

如果您查看 _pyio.py(io 模块的 pure-Python 实现),您会在 class 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__ 处理什么?没有什么。它只是 returns 文件对象,它是调用 built-in 函数 open() 的结果。

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

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

是否应该为自定义 functions/context 经理实施此模式?任何时候你有一个对象需要清理,或者使用涉及某种类型的 open/close 概念,你应该考虑这个模式。标准库还有许多其他示例。

对于问题 1

在 CPython 中,open() 除了创建一个 file 对象外什么都不做,底层 C 类型是PyFileObject;请参阅 bltinmodule.c and fileobject.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 是分开的。