python中的上下文管理器和with语句块

上下文管理器对象存在的目的就是管理with语句。上下文管理器协议包含__enter__ 和__exit__ 两个方法。with 语句开始运行时,会在上下文管理器对象上调用__enter__ 方法。with 语句运行结束后,会在上下文管理器对象上调用__exit__ 方法

来看一个例子,把文件对象当成上下文管理器使用

with open('test.dat') as fp:
    secc = fp.read(20)
>>> len(src)
20
>>> fp 
<_io.TextIOWrapper name='mirror.py' mode='r' encoding='UTF-8'>
>>> fp.closed, fp.encoding 
(True, 'UTF-8')
"""
但是不能在fp 上执行I/O 操作,因为在with 块的末尾,调用TextIOWrapper.__exit__
方法把文件关闭了。
"""
>>> fp.read(60)
Traceback (most recent call last):
File "", line 1, in <module>
ValueError: I/O operation on closed file.

执行with后面的表达式得到的结果是上下文管理器对象,不过,把值绑定到目标变量上(as子句)是上下文管理器调用__enter__方法的结果

来看一个例子,自己实现一个上下文管理器

class Sample():
    def __enter__(self):
        print("in __enter__")
        return self
        
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("in __exit__")
        
    def do_something(self):
        print("i am doing something")
        
>>> with Sample() as sample:
        sample.do_something()
        
in __enter__
i am doing something
in __exit__

真个过程运行如下,enter()方法被执行,enter()方法返回值,在这里是类自身,执行代码块sample.do_something(), 最后是exit()方法被调用

contextlib模块中的使用工具

@contextmanager 装饰器能减少创建上下文管理器的样板代码量,因为不用编写一个完整的类,定义__enter__ 和__exit__方法,而只需实现有一个yield 语句的生成器,生成想让__enter__ 方法返回的值。
在使用@contextmanager 装饰的生成器中,yield语句的作用是把函数的定义体分成两部分**:yield 语句前面的所有代码在with 块开始时(即解释器调用__enter__方法时)执行,yield 语句后面的代码在with 块结束时(即调用__exit__ 方法时)执行**

看一个使用contextmanager实现上下文管理器的例子

import contextlib
@contextlib.contextmanager
def Sample():
    def do_something():
        print('i am doing something')
    print("in __enter__")
    yield do_something
    print("in exit")
    
>>> with Sample() as sample:
        sample()
        
in __enter__
i am doing something
in __exit__

你可能感兴趣的:(python)