在Python中让自己创建的函数、类、对象支持with语句,就实现了上线文管理协议。我们经常使用with open(file, "a+") as f:这样的语句,无需手动调用f.close()关闭文件。这种用法不仅优雅,而且避免遗忘释放资源,十分方便。所以,当操作某些资源时,需要对资源的获取与释放进行自动操作,就可以用上线文管理器。比如:数据库的连接,查询,关闭处理;socket的连接和断开。本篇主要介绍,如何让自己创建的类、对象、函数等支持with语句,详细请看下文。

1 让对象支持上下文管理协议

在类中,实现 __enter__()和__exit__()方法,类创建的对象就支持with语句。如下:

class A:
def __enter__(self):
print("in __enter__")
return [1, 2, 3] def __exit__(self, exc_type, exc_val, exc_tb):
print("in __exit__") obj = A()
with obj as o:
print("看看o是什么:", o)
print("do something")
print("end")  

注意本例的输出结果的顺序:

in __enter__
看看o是什么: [1, 2, 3]
do something
in __exit__
end

(1)当执行 with 语句的时候,对象的 __enter__() 方法被触发, 它返回的值(如果有的话)会被赋值给 as 声明的变量。对应输出应该是【in __enter__】和将[1,2,3]赋值给o【字母o】。

(2)然后,with 语句块里面的代码开始执行。对应的输出是【看看o是什么: [1, 2, 3]】和【do someting】。

(3)最后,__exit__() 方法被触发进行清理工作。对应的输出是【in __exit__】。

补充说明:
__exit__()方法的第三个参数包含了异常类型、异常值和追溯信息(如果有的话)。 __exit__()方法能自己决定怎样利用这个异常信息,或者忽略它并返回一个None值。

如果 __exit__() 返回 True ,那么异常会被清空,就好像什么都没发生一样, with 语句后面的程序继续在正常执行。

上面的例子还不支持多个with嵌套使用,下面是一个可以嵌套使用with语句的例子:

from socket import socket, AF_INET, SOCK_STREAM

class Connection:
def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
self.address = address
self.family = family
self.type = type
self.connections = [] def __enter__(self):
sock = socket(self.family, self.type)
sock.connect(self.address)
self.connections.append(sock)
return sock def __exit__(self, exc_ty, exc_val, tb):
self.connections.pop().close() conn = Connection(('www.python.org', 80))
with conn as s1:
print(s1)
with conn as s2:
print(s2)

2 装饰器版上下文管理器

上面介绍了在类和对象中实现上下文管理协议,其实Python标准库中contextlib包的@contextmanager装饰器能够轻松实现一个上下文管理器,下例是用其实现统计代码块耗时的上下文管理器:

import time
from contextlib import contextmanager
# 来看一个装饰器版本的上下文管理器
# 检查代码消耗时间块
@contextmanager
def timecount(name):
start = time.time()
try:
yield
finally:
end = time.time()
print('{}: {}'.format(name, end - start)) with timecount('cost time:'):
time.sleep(2)
# cost time:: 2.000200033187866

timecount()中,yield之前的代码相当于__enter__()方法;yield 之后的代码相当于 __exit__()方法。如果有异常会自动在yield一行抛出。

上下文管理器可以应用在事务中:

# 更高级的事务管理
@contextmanager
def list_transaction(orig_list):
working = list(orig_list)
yield working
orig_list[:] = working lis = [1, 2, 3]
with list_transaction(lis) as work:
work.append(5)
work.append(6) print(lis)
# [1, 2, 3, 5, 6]  

一旦with语句内有异常产生,之前的操作不会生效:

lis = [1, 2, 3]
with list_transaction(lis) as work:
work.append(5)
work.append(6)
print(lis)
raise RuntimeError("回滚")  

如果在交互式命令行中打印lis,依然会发现lis的内容没有改变,也就是说,with语句中的代码出现异常,with语句中的操作都不会生效,只有with无异常退出,才会生效。对于事务管理来说是比较有用的。

3 小结

(1)当操作一些需要打开、连接、断开或释放的资源时,让对象或函数支持with语句十分方便、省事、优雅。如数据库的链接断开、套接字的连接断开、事务、锁资源等。

(2)类中实现__enter__()和__exit__()方法,即可实现上下文管理协议,对象可使用with语句。

(3)通过contextlib包中的@contextmanager装饰器装饰一个函数,该函数即可使用with语句。

  

 

 

Python上下文管理器的更多相关文章

  1. python 上下文管理器

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 上下文管理器(context manager)是Python2.5开始支持的一种语 ...

  2. Python 上下文管理器和else块

    最终,上下文管理器可能几乎与子程序(subroutine)本身一样重要.目前,我们只了解了上下文管理器的皮毛--Basic 语言有with 语句,而且很多语言都有.但是,在各种语言中 with 语句的 ...

  3. python上下文管理器ContextLib及with语句

    http://blog.csdn.net/pipisorry/article/details/50444736 with语句 with语句是从 Python 2.5 开始引入的一种与异常处理相关的功能 ...

  4. Python上下文管理器 with

    对于系统资源的操作,如:文件操作.数据库操作等,我们往往打开文件.连接数据库后忘了将其close掉,这时就可能会引发异常,因此我们常用的做法是: # coding:utf-8 f = open(&qu ...

  5. Python上下文管理器(Context managers)

    上下文管理器(Context managers) 上下文管理器允许你在有需要的时候,精确地分配和释放资源. 使用上下文管理器最广泛的案例就是with语句了.想象下你有两个需要结对执行的相关操作,然后还 ...

  6. python上下文管理器细读

    test 1 上下文管理器,将生成器转化为上下文管理器 import contextlib @contextlib.contextmanager def a(): print(1) yield pri ...

  7. 吃透Python上下文管理器

    什么是上下文管理器? 我们常见的with open操作文件,就是一个上下文管理器.如: with open(file, 'rb') as f: text = f.read() 那上下文管理器具体的定义 ...

  8. python 上下文管理器contextlib.ContextManager

    1 模块简介 在数年前,Python 2.5 加入了一个非常特殊的关键字,就是with.with语句允许开发者创建上下文管理器.什么是上下文管理器?上下文管理器就是允许你可以自动地开始和结束一些事情. ...

  9. Python 上下文管理器模块--contextlib

    在 Python 处理文件的时候我们使用 with 关键词来进行文件的资源关闭,但是并不是只有文件操作才能使用 with 语句.今天就让我们一起学习 Python 中的上下文管理 contextlib ...

随机推荐

  1. Unity引擎与C#脚本简介

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 由 QQ会员技术团队 发布在云+社区 1. Unity编辑器基础 从原理上讲,游戏开发就是将一系列变动的场景呈现在玩家面前,并根据玩家的输入 ...

  2. 使用java实现阿里云消息队列简单封装

    一.前言 最近公司有使用阿里云消息队列的需求,为了更加方便使用,本人用了几天时间将消息队列封装成api调用方式以方便内部系统的调用,现在已经完成,特此记录其中过程和使用到的相关技术,与君共勉. 现在阿 ...

  3. TensorFlow与主流深度学习框架对比

    引言:AlphaGo在2017年年初化身Master,在弈城和野狐等平台上横扫中日韩围棋高手,取得60连胜,未尝败绩.AlphaGo背后神秘的推动力就是TensorFlow--Google于2015年 ...

  4. VS2010 C++学习(5):基于DirectShow的视频预览录像程序

    VS2010 C++学习(5):基于DirectShow的视频 预览录像程序 学习VC++编制的基于DirectShow视频捕获程序,主要练习基于DirectShow程序的应用. 一.         ...

  5. linux下ffmpeg安装

    1.ffmpeg下载地址: http://www.ffmpeg.org/download.html 2.解压 1 $ tar zvfj ffmpeg.tar.bz2 这里作者假设已经重命名为ffmpe ...

  6. dojo中引入FusionCharts柱状图报错

    1.今天,做项目的过程中,我发现Java后台查询的数据都是正确的,并且拼接成JSON格式也正确,但是传到JSP页面时,图无法显示出来还报错,后来经过检查发现是JavaScript和引入FusionCh ...

  7. SOCKET 编程TCP/IP、UDP

    TCP/IP 资源:http://download.csdn.net/detail/mao0514/9061265 server: #include<stdio.h> #include&l ...

  8. 如何利用JavaScript遍历JSON数组

    1.设计源码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www. ...

  9. ArgumentError:Error #2004:某个参数无效

    1.错误描述 ArgumentError:Error #2004:某个参数无效 at flash display::Graphics/drawRect() at ZeroClipboard() 2.错 ...

  10. Django学习-4-request获取数据

    app下views.py             获取前端HTML数据的一些方法             def func(request):                 # request.me ...