欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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