Python open如何处理上下文管理?

Python open如何处理上下文管理?,python,file,with-statement,contextmanager,Python,File,With Statement,Contextmanager,python内置的open和file以一种我不太理解的方式与上下文管理器一起工作 据我所知,open将创建一个文件文件实现上下文管理器方法\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。我最初希望\uuuu\uuuu\uuuu实现文件描述符的实际打开 但是,在带有块的之外使用打开将返回一个已打开的文件。因此,它显示为file.\uuuu init\uuu或open实际上正在打开文件描述符,据我所知file.\uu enter\uuuu没有做任何事情。或者可能是文件。/打开调用

python内置的
open
file
以一种我不太理解的方式与上下文管理器一起工作

据我所知,
open
将创建一个
文件
<代码>文件实现上下文管理器方法\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。我最初希望
\uuuu\uuuu\uuuu
实现文件描述符的实际打开

但是,在带有块的
之外使用
打开
将返回一个已打开的
文件。因此,它显示为
file.\uuuu init\uuu
open
实际上正在打开文件描述符,据我所知
file.\uu enter\uuuu
没有做任何事情。或者可能是
文件。
/
打开
调用
文件。
直接输入

第一个问题:

open
内置的执行流程是什么?
打开
处理什么,
文件处理什么,以及
文件处理什么,输入
处理什么?当重复使用一个
文件
对象多次打开/关闭文件时,这是如何工作的?这与在多个上下文周期中重复使用其他contextmanager对象有何不同

第二个问题:

对象(如
文件
对象)具有设置步骤和拆卸步骤。设置发生在
\uuuuu init\uuuuuu
中,拆卸发生在
关闭
退出

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

如果查看_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.\u\u输入\u
处理什么?没有什么。它只是将调用内置函数open()的结果file对象返回给您

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

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


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

对于问题1

在CPython中,open()除了创建一个文件对象之外,什么也不做,底层的C类型是PyFileObject;请参阅和中的源代码

  • file.\uuuu init\uuuu
    将打开该文件

  • file.\uuuu输入\uuuu
    除了对字段
    file.fp执行空检查外,实际上什么都不做

  • file.\uuu退出\uu
    调用
    close()
    方法关闭文件

对于问题2

为什么
文件
设计成这样是因为历史原因

openwith是在不同版本的CPython上引入的两个不同关键字with一直引入到Python 2.5(参见)。当时,open已经使用了很长时间

对于我们的定制类型,我们可以像文件那样设计,也可以不设计,这取决于具体的应用程序上下文


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

Python是开源的,这意味着你可以通过查看它来了解它是如何实现的,并回答你自己的问题。我认为现在是首选
static PyObject *
builtin_open(PyObject *self, PyObject *args, PyObject *kwds)
{
    return PyObject_Call((PyObject*)&PyFile_Type, args, kwds);
}