经常在Python代码中看到with语句,仔细分析下,会发现这个with语句功能好强,可以自动关闭资源。这个在Python中叫上下文管理器Context Manager。那我们要怎么用它,什么时候用它呢。这里我们就来聊一聊。

上下文管理器的作用

很多情况,当我们使用完一个资源后,我们需要手动的关闭掉它,比如操作文件,建立数据库连接等。但是,在使用资源的过程中,如果遇到异常,很可能错误被直接抛出,导致来不及关闭资源。所以在大部分程序语言里,我们使用”try-finally”语句来确保资源会关闭。比如下面的Python写文件代码:

 
1
2
3
4
5
try:
    f = open('test.txt', 'a+')
    f.write('Foo\n')
finally:
    f.close()

这样做固然没有问题,但是当”try-finally”中间的逻辑复杂,而且还带有各种嵌套的话,代码就很不容易维护。Python的with语句,可以说功能同上面的”try-finally”几乎一样,但代码看上去简洁的多,我们来实现同样的功能:

 
1
2
with open('test.txt', 'a+') as f:
    f.write('Foo\n')

with语句后面跟着open()方法,如果它有返回值的话,可以使用as语句将其赋值给f。在with语句块退出时,”f.close()”方法会自动被调用,即使”f.write()”出现异常,也能确保close()方法被调用。

自定义类来使用上下文管理器

上例中”open()”方法是Python自带的,那我们怎么定义自己的类型来使用with语句呢。其实只要你的类定义了”__enter__()”和”__exit__()”方法,就可以使用Python的上下文管理器了。”__enter__()”方法会在with语句进入时被调用,其返回值会赋给as关键字后的变量;而”__exit__()”方法会在with语句块退出后自动被调用。

我们来实现个跟上节一样的文件写入功能:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
class OpenFileDemo(object):
    def __init__(self, filename):
        self.filename = filename
 
    def __enter__(self):
        self.f = open(self.filename, 'a+')
        return self.f
 
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()
 
with OpenFileDemo('test.txt') as f:
    f.write('Foo\n')
异常处理

肯定有朋友注意到上面的”__exit__()”带了三个参数,是的,他们是用来异常处理的。大部分情况下,我们希望with语句中遇到的异常最后被抛出,但也有时候,我们想处理这些异常。”__exit__()”方法中的三个参数exc_type, exc_val, exc_tb分别代表异常类型,异常值,和异常的Traceback。当你处理完异常后,你可以让”__exit__()”方法返回True,此时该异常就会不会再被抛出。比如我们将上例中的”__exit__()”方法改一下:

 
1
2
3
4
5
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()
        if exc_type != SyntaxError:
            return True
        return False   # Only raise exception when SyntaxError

现在,如果遇到SyntaxError的话,异常会被正常抛出,而其他异常的话都会被忽略。

contextlib模块

Python中还有一个contextlib模块提供一些简便的上下文管理器功能。

CLOSING()方法

如果说with语句块在退出时会自动调用”__exit__()”方法的话,那用了”contextlib.closing()”的with语句块则在退出时会自动调用”close()”方法。看一下示例:

 
1
2
3
4
5
6
7
8
9
10
11
import contextlib
 
class Resource(object):
    def open(self):
        print 'Open Resource'
 
    def close(self):
        print 'Close Resource'
 
with contextlib.closing(Resource()) as r:
    r.open()

程序运行后,会打印出

Open Resource
Close Resource

说明Resource类创建的对象被赋给了as关键字后面的变量r,而with语句块退出时,自动调用了”r.close()”方法。

CONTEXTMANAGER装饰器

“@contextlib.contextmanager”是一个装饰器,由它修饰的方法会有两部分构成,中间由yield关键字分开。由此方法创建的上下文管理器,在代码块执行前会先执行yield上面的语句;在代码块执行后会再执行yield下面的语句。看个例子比较容易明白:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
import contextlib
import time
 
@contextlib.contextmanager
def timeit():
    start = time.time()
    yield
    end = time.time()
    usedTime = (end - start) * 1000
    print 'Use time %d ms' % usedTime
 
with timeit():
    time.sleep(1)

这个”timeit()”方法实现了一个计时器,它会计算由他生成的with语句块执行时间。可以看出,yield上面的语句就如同之间介绍过的”__enter__()”方法,而yield下面的语句就如同”__exit__()”方法。而yield部分就是with语句块中的代码。如果yield后面带参数的话,我们就可以用as关键字赋值给后面的变量,比如上例:

 
1
2
3
4
5
6
7
8
9
@contextlib.contextmanager
def timeit():
    start = time.time()
    yield start
    #...
 
with timeit() as starttime:
    print starttime
    #...

需要注意的是,”@contextlib.contextmanager”不像之前介绍的”__exit__()”方法,遇到异常也会执行。也就是with语句块抛出异常的话,yield后面的代码将不会被执行。所以,必要时你需要对yield语句使用”try-finally”。

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

  1. python contextlib 上下文管理器

    1.with操作符 在python中读写文件,可能需要这样的代码 try-finally读写文件 file_text = None try: file_text = open('./text', 'r ...

  2. python使用上下文管理器实现sqlite3事务机制

    如题,本文记录如何使用python上下文管理器的方式管理sqlite3的句柄创建和释放以及事务机制. 1.python上下文管理(with) python上下文管理(context),解决的是这样一类 ...

  3. 【Python】 上下文管理器和contextlib

    上下文管理器 一直对python中的上下文管理比较迷惑,趁着今天研究SQLAlchemy顺便看了一下,感觉稍微清楚了一点.http://www.cnblogs.com/chenny7/p/421344 ...

  4. python 黑魔法 ---上下文管理器(contextor)

    所谓上下文 计算机上下文(Context)对于我而言,一直是一个很抽象的名词.就像形而上一样,经常听见有人说,但是无法和现实认知世界相结合. 最直观的上下文,莫过于小学的语文课,经常会问联系上下文,推 ...

  5. python的上下文管理器-1

    reference:https://zhuanlan.zhihu.com/p/26487659 来看看如何正确关闭一个文件. 普通版: def m1(): f = open("output. ...

  6. Python 的上下文管理器是怎么设计的?

    花下猫语:最近,我在看 Python 3.10 版本的更新内容时,发现有一个关于上下文管理器的小更新,然后,突然发现上下文管理器的设计 PEP 竟然还没人翻译过!于是,我断断续续花了两周时间,终于把这 ...

  7. python的上下文管理器

    直接上代码: f = open('123.txt','w') try: f.write('hello world') except Exception: pass finally: f.close() ...

  8. 【Python】【上下文管理器】

    """#[备注]#1⃣️try :仅当try块中没有异常抛出时才运行else块.#2⃣️for:仅当for循环运行完毕(即for循环没有被break语句终止)才运行els ...

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

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

随机推荐

  1. JavaScript创建对象的几种方式总结

    ECMA把对象定义为:无序属性的集合,其属性可以包含基本值.对象或者函数. 1. 使用Object构造函数创建对象 创建自定义对象的最简单的方式就是创建一个Object的实例,然后再为它添加属性和方法 ...

  2. http方式nginx 访问不带www的域名301重定向跳转到www的域名帮助seo集中权重

    比如我需要吧gucanhui.com重定向301跳转到www.gucanhui.com 需要在nginx的con发文件中加入一段 server { listen ; server_name gucan ...

  3. 超高频率问题之信息: Illegal access: this web application instance has been stopped already. Could not load . The eventual following stack trace is caused by an error thrown for debugging purposes as well as

    出现频率非常高,目前还不确定具体是什么原因导致

  4. Redis数据库在ubuntu16.04下的安装

    1.安装 sudo apt-get install redis-server 2.启动 sudo service redis-server start 3.查看 ps aux|grep redis 4 ...

  5. JavaScript--jquery.min.js文件

    /*! jQuery v1.12.3 | (c) jQuery Foundation | jquery.org/license */ !function(a,b){"object" ...

  6. SVN经常使用操作

    版权声明:本文为博主原创文章.转载请注明出处. https://blog.csdn.net/Jerome_s/article/details/27950055 Subversion(简称svn)安装 ...

  7. idea启动报错:Access denied for user 'root '@'192.168.100.XXX' (using password: YES)

    Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean wit ...

  8. console 中的格式化打印(占位符),和样式定义

    格式化打印 Gecko 9.0 (Firefox 9.0 / Thunderbird 9.0 / SeaMonkey 2.6) 首次发布对string substitutions的支持.你可以在传递给 ...

  9. iOS常量(const)、enum以及宏(#define)

    http://www.cocoachina.com/ios/20160530/16483.html 本文投稿文章,作者:SuperMario_Nil(简书) 前言:本文主要梳理iOS中如何使用常量.e ...

  10. tomcat的日志不输出日志信息的解决方法

    1.下载日志jar包,例如:commons-logging-1.1.1.jar.放在tomcat的bin目录下,或者是自己项目的lib包里. 2.修改tomcat的bin目录下面的catalina.b ...