Python上下文管理器Context Manager

Python上下文管理器Context Manager

链接:www.bjhee.com/python-context.html

上下文管理器的作用

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

try:

    f = open('test.txt', 'a+')

    f.write('Foo\n')

finally:

    f.close()

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

with open('test.txt', 'a+') as f:

    f.write('Foo\n')

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

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

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

实现文件写入功能:

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__()”方法改一下:

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()”方法。看一下示例:

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下面的语句。看个例子比较容易明白:

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关键字赋值给后面的变量,比如上例:

@contextlib.contextmanager

def timeit():

    start = time.time()

    yield start

    #...

with timeit() as starttime:

    print starttime

    #...

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值