python with关键字做了些啥?
程序员文章站
2022-04-19 12:32:45
python 上下文管理协议我们时常会使用如下的代码with open('1.txt') as f: print(f.readlines())我们被告知这样可以“安全”的打开一个文件。因为当代码执行超出 with 的作用域时文件会自动关闭。那这是怎么做到的呢。这就涉及到 python 上下文管理协议。一个对象的实现使用了一对专门方法。__enter__:该方法在进入上下文时被调用,要求返回一个对象,被 as 关键字后的变量所引用。__exit__:该方法在退出上下文时被调用, 一般...
python 上下文管理协议
我们时常会使用如下的代码
with open('1.txt') as f:
print(f.readlines())
我们被告知这样可以“安全”的打开一个文件。因为当代码执行超出 with 的作用域时文件会自动关闭。
那这是怎么做到的呢。这就涉及到 python
上下文管理协议。
一个对象的实现使用了一对专门方法。__enter__
:该方法在进入上下文时被调用,要求返回一个对象,被 as 关键字后的变量所引用。__exit__
:该方法在退出上下文时被调用, 一般用于清理当前上下文环境。
如上面的代码中。我们知道 open 函数会返回一个文件对象。而这个对象就实现了___enter__
,__exit__
方法,而后者中就包含自动将文件对象关闭的代码。
因为这样写能安全的打开和关闭文件。而不再需要自己手动关闭,这样防止自己忘记关闭文件对象。
print(dir(open('1.txt')))
# 输出结果
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'write_through', 'writelines']
可以看到 open 函数返回的文件对象实现了__enter__
和 __exit__
方法。
接来下我们自己实现一个满足上下文管理协议的对象吧。
import sys
class A:
def __init__(self, stdout=None):
from io import StringIO
self.stdout = stdout or StringIO()
self.old = sys.stdout
def __enter__(self):
sys.stdout = self.stdout
return self.stdout
def __exit__(self,xc_type, exc_val, exc_tb):
sys.stdout = self.old
with A() as stdout:
print('inner')
print('outter')
#输出结果
outter
这段代码中,在 with 上下文中,能修改了标准输出,因此中间的print语句都不会打印到屏幕中。当脱离上下文时自动恢复。
好像有点意思,但是,这还远远不够哦。因为要实现一个上下文协议,貌似还挺麻烦的。
python 提供了一个装饰器用于快速实现。
import contextlib
@contextlib.contextmanager
def A(stdout=None):
old = sys.stdout
if stdout is None:
from io import StringIO
stdout = StringIO()
sys.stdout = stdout
yield stdout
sys.stdout = old
with A() as stdout:
print('inner')
print('outter')
这段代码的作用和上一段等效
yield 之前的代码相当于`__enter__`函数, yield 之后的代码相当于 `__exit__`函数。
学会了吗?
- 思考题实现一个上下文管理器,进入 with 上下文和离开的时候自动打印一些log 日志。
本文地址:https://blog.csdn.net/yyonging/article/details/107162773