Home >Backend Development >Python Tutorial >Python 中 open() 方法既能直接返回也能通过with语句当作上下文管理器使用是怎么做到的?

Python 中 open() 方法既能直接返回也能通过with语句当作上下文管理器使用是怎么做到的?

WBOY
WBOYOriginal
2016-06-06 16:24:091138browse

如题。简单看了下io.py部分的源码,只看到了open的定义是直接返回对象,没有看到是如何实现上下文管理器的。Google了半天也没有结果。求知乎大神解答!

回复内容:

前段时间果壳 Python 开发面试被问到了这个问题

实现某个对象可以用 with 来管理,只需要改写 __enter__ 和 __exit__ 这两个 magic method 即可

另外你说你在 io.py 源码里没找到,大哥读代码要仔细啊

io.py 里的 IO 函数都是从 _pyio.py 里 import 进来的,然后在 _pyio.py 的第 140 行 到 147 行有这么一段注释

open() returns a file object whose type depends on the mode, and
through which the standard file operations such as reading and writing
are performed. When open() is used to open a file in a text mode ('w',
'r', 'wt', 'rt', etc.), it returns a TextIOWrapper. When used to open
a file in a binary mode, the returned class varies: in read binary
mode, it returns a BufferedReader; in write binary and append binary
modes, it returns a BufferedWriter, and in read/write mode, it returns
a BufferedRandom.

所以 open() 这个函数在不同模式下会返回不同的对象,包括 TextIOWrapper/BufferedReader/BufferedWriter 这三种

继续看上面这三个类的定义,发现这几个类还继承了其他的几个类,这里我就不领着你看了,直接用 Visio 画了个类层次示意图,如下

Python 中 open() 方法既能直接返回也能通过with语句当作上下文管理器使用是怎么做到的?
其中蓝色矩形表示 class,蓝色连线表示继承关系,蓝色连线上的数字表示这个继承关系的代码在源码中的哪一行

可以看到 open() 函数所返回的几个 class,最终都继承于 IOBase 这个 class,而在这个 class 中,就实现了 __enter__() 和 __exit() 两个 magic method,具体代码位于 _pyio.py 的第 428 行到 437 行

<code class="language-python">    <span class="c">### Context manager ###</span>

    <span class="k">def</span> <span class="nf">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">"""Context management protocol.  Returns self."""</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">_checkClosed</span><span class="p">()</span>
        <span class="k">return</span> <span class="bp">self</span>

    <span class="k">def</span> <span class="nf">__exit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">):</span>
        <span class="sd">"""Context management protocol.  Calls close()"""</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</code>
只要有__enter__和__exit__方法,就能用在with语句里,open返回的对象有这两个方法,就可以了,不必在open方法内做什么手脚。 Python 的上线文管理器使用with关键字,用来定义with后面这个缩进级别,进入和跳出的行为,是语法糖的一种。open是一个调用用于实例化file类的函数,而file类只不过是一个拥有__enter__和__exit__(这种前后都有两个下划线的在Python中叫做“魔法方法”,除了这两个还有很多,有兴趣可以去查一下)的类,用在with中时,程序会自动在进出这个程序块时调用这两个函数,如果没用with就要手动管理,自己调用close()。如果想要自己实现类似的功能,用回调就,或者单独封装成装饰器(装饰器其实也是Python的一种语法糖)都可以。 跟Java的Closeable一样 定义好释放资源的方式 通过语法糖隐藏部分重复代码 你google context manager就有结果了
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn