当前位置: 移动技术网 > IT编程>脚本编程>Python > python with关键字做了些啥?

python with关键字做了些啥?

2020年07月07日  | 移动技术网IT编程  | 我要评论

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网