博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python上下文管理器
阅读量:6041 次
发布时间:2019-06-20

本文共 4199 字,大约阅读时间需要 13 分钟。

上下文管理器和with模块

上下文管理器对象存在的目的是管理with语句。with语句的目的是简化try/finally模式。这种模式用于保证一段代码运行完毕后执行某项操作,即便那段代码异常,return语句调用或sys.exit()调用而中止,也会执行操作。finally子句中的代码通常用于释放重要的资源,或者还原临时变更的状态。

 

上下文管理器包含__enter__和__exit__两个方法。with语句开始运行时,会在上下文管理器对象上调用__enter__方法。with语句运行结束后,会在上下文管理器对象上调用__exit__方法,以此扮演finally子句的角色

创建一个mirror.py文件,演示把文件对象当成上下文管理器使用:

with open('mirror.py') as fp:  #fp绑定到打开的文件上,因为文件的方法返回self    src = fp.read(60)              #读取数据len(src)print(fp) print(fp.closed)print(fp.encoding)print(fp.read(60))#结果<_io.TextIOWrapper name='mirror.py' mode='r' encoding='cp936'>   #fp对象仍然可用True             cp936          #读取fp对象的属性ValueError: I/O operation on closed file.   #with块的末尾,调用TextIOWrapper.__exit__方法把文件关闭了

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

示例中open()函数返回TextIOWrapper类实例,而该实例的__enter__方法返回self,__enter__方法除了会返回上下文管理器对象之外,还可能返回其他对象。

不管控制流程以哪种方式退出with块,都会在上下文管理器对象上调用__exit__方法,而不是在__enter__方法返回的对象上调用。

with语句的as子句是可选的。对于open()函数来说,必须加上as子句以便获取文件引用。而有些上下文管理器会返回None。

一个上下文管理器类:

class LookingGlass:    def __enter__(self):  #除了self不传入其他参数        import sys        self.original_write = sys.stdout.write   #把原来的sys.stdout.write保存在一个实例属性中        sys.stdout.write = self.reverse_write    #替换成自己编写的方法        return 'JABBERWOCKY'                     #返回字符串    def reverse_write(self, text):          #内容反转打印        self.original_write(text[::-1])    def __exit__(self, exc_type, exc_val, exc_tb):          import sys        sys.stdout.write = self.original_write   #将方法改回        if exc_type is ZeroDivisionError:     #异常检测            print('please DO NOT divide by zero!')            return True

解释器调用__enter__方法时,除了隐式的self之外,不会传入任何参数。传递给__exit__的有三个参数:

exc_type              //异常类

exc_value            //异常实例。有时会有参数传递给异常构造方法,这些参数可以使用exc_value.args获取

traceback             //traceback对象

测试:

from mirror import LookingGlasswith LookingGlass() as what:   #上下文管理器上调用__enter__方法,把返回结果绑定到what上    print('hello world!')    print(what)print(what)print('hello world!')

结果:

!dlrow ollehYKCOWREBBAJ    #打印出的内容是反向的JABBERWOCKY    #with语句执行完毕,输出不再反向hello world!

也可用在with块之外使用LookingGlass类:

from mirror import LookingGlassmanager = LookingGlass()      #实例化print(manager)      #审查对象moster = manager.__enter__()   #调用__enter__print(moster == 'JABBERWOCKY')   #见结果,输出反向print(moster)           #输出反向print(manager)        #输出反向manager.__exit__(None, None, None)  #调用__exit__还原函数print(moster == 'JABBERWOCKY')print(moster)#结果
eurTYKCOWREBBAJ>8B7465A4F6100000x0 ta tcejbo ssalGgnikooL.rorrim

contextlib模块中的实用工具

closing                          //如果对象提供了close方法,但没有实现__enter__/__exit__协议,那么可用使用这个函数构建上下文管理器

suppress                       //构建临时忽略指定异常的上下文管理器

@contextmanager          //这个装饰器把简单的生成器函数变为上下文管理器

ContextDecorator         //这是个基类,用于定义基于类的上下文管理器。这种上下文管理器也能用于装饰函数,在受管理的上下文中运行整个函数。

ExitStack                      //这个上下文管理器能进入多个上下文管理器。with块结束时,ExitStack按照后进先出的顺序调用栈中各个上下文管理器的__exit__方法。

@contextmanager

示例,类似Lookingglass:

@contextlib.contextmanager   #应用装饰器def looking_glass():    import sys    original_write = sys.stdout.write        def reverse_write(text):        original_write(text[::-1])            sys.stdout.write = reverse_write()    yield 'JABBERWOCKY'   #产出一个值,这个值会绑定在with语句中as子句的目标变量上。执行with语句块中代码时,这个函数会在这个点暂停    sys.stdout.write = original_write    #控制权跳出with块,继续执行yield语句之后的代码

@contextlib.contextmanager装饰器会把函数包装成实现__enter__和__exit__方法的类。

1.这个类__enter__方法有如下作用
(1)调用生成器函数,保存生成器对象
(2)调用next(),执行到yield关键字所在位置
(3)返回next()产出的值,以便把产出的值绑定的with/as语句中的目标变量上

2.with块终止时,__exit__方法会做以下几件事情

(1)检查有没有把异常传递给exc_type;如果有,调用gen.throw(exception),在生成器函数定义体中包含yield关键字的那一行抛出异常

(2)否则调用next()函数,继续执行生成器函数定义体中的yield语句之后的代码。

上面looking_glass()函数有一个错误,2(1)步骤如果抛出错误,却没有错误处理代码,将无法执行sys.stdout.write = original_write。

添加异常处理代码:

@contextlib.contextmanager   #应用装饰器def looking_glass():    import sys    original_write = sys.stdout.write    def reverse_write(text):        original_write(text[::-1])    sys.stdout.write = reverse_write()    msg = ''    try:        yield 'JABBERWOCKY'    except ZeroDivisionError:        msg = 'Please DO NOT divide by zero!'    finally:        sys.stdout.write = original_write        if msg:            print(msg)

使用@contextmanager装饰器时,要把yield语句放在try/finally语句中(或者with语句中),因为永远不知道用户会在上下文管理器做什么。

以上来自《流畅的python》

转载于:https://www.cnblogs.com/lht-record/p/10328069.html

你可能感兴趣的文章
如何UDP/TCP端口是否通了
查看>>
pxe实现系统的自动化安装
查看>>
Redis高可用技术解决方案总结
查看>>
Scale Out Owncloud 高可用(2)
查看>>
何为敏捷
查看>>
HA集群之四:Corosync+Pacemaker+DRBD实现HA Mysql
查看>>
服务器定义
查看>>
我的友情链接
查看>>
MYSQL-实现ORACLE- row_number() over(partition by ) 分组排序功能
查看>>
c# 入门 例子
查看>>
HP Designjet 800PS 日常维护
查看>>
rhel7使用fdisk分区时无法使用全部分区的解决办法
查看>>
Docker 清理命令
查看>>
利用NRPE外部构件监控远程主机
查看>>
使用模块化编译缩小 apk 体积
查看>>
router-link传参
查看>>
ios之UISlider
查看>>
短信验证流程
查看>>
php 使用htmlspecialchars() 和strip_tags函数过滤HTML标签的区别
查看>>
OpenCV Error: Assertion failed (data0.dims <= 2 && type == 5 && K > 0) in cv::kmeans
查看>>