Python中的上下文管理器和else块

导语:本文章记录了本人在学习Python基础之控制流程篇的重点知识及我的心得,打算入门Python的朋友们能够来一块儿学习并交流。

本文重点:python

一、掌握if语句以外的else的用法;
二、掌握上下文管理器的定义、协议、使用和with块;
三、掌握有用的@contextmanger装饰器。

1、if语句以外的else块

一、else块介绍

if/else中if和else是同级对立的语句,对立是指流程通过一层if/else语句只能对应一种处理语句。而else在for/else,while/else,try/else语句中的功能则大相径庭。后者中的else功能以下:数据库

  • for/else:for循环没有被break语句停止才运行else块。
  • while/else:while循环没有被break语句停止才运行else块。
  • try/else:try块中,没有异常抛出时才运行else块。

下面以for/else为例进行代码实现:编程

for i in "apple":
    if i.isupper():
        break
else:
    raise ValueError('No upper string was found')

二、try/else块背后的编程风格

try/except不只用于处理错误,还用于处理错误,这属于EAFP编程风格。app

  • EAFP:easier to ask for forgiveness than permission
    取得原谅比得到许可容易。即先假定存在有效的键或属性,若是假定不成立,那么捕获异常。
  • LBYL:look before you leap
    三思然后行。即在调用函数或查找属性或键以前显式测试前提条件。

2、上下文管理器和with块

一、上下文管理器介绍

上下文管理器(context manger):在操做文件和创建数据库链接的时候,咱们最终须要关闭资源,这就是上下文管理器存在的意义。
上下文管理器协议:包含__enter__和__exit__两个方法。
语法:try/finally模式和with语句
实例:try/finally模式函数

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

接下来咱们用with语句进行替换:
实例:with语句工具

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

分析:open 的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法。
总结:with块的功能在于简化try/finally模式。with语句在开始运行时会在上下文管理器对象上调用__enter__,而with语句结束时会调用__exit__方法。
Tips:with语句中的as语句是可选的,as语句将__enter__返回的值绑定到as语句后的变量。值得注意的是,对于open函数必须加上as字句来获取文件的引用。学习

二、自定义上下文管理器

在掌握基本上下文管理器和with语句后,咱们经过自定义上下文管理器来深入认识with语句和__enter__以及__exit__的联系。
实例:自定义知足上下文管理器协议的类测试

class OpenFileDemo(object):
    def __init__(self, filename):
        self.filename = filename
 
        self.f = open(self.filename, 'a+')
        return self.f
 
    def __exit__(self, exc_type, exc_value, traceback):
        self.f.close()
        if exc_type != SyntaxError:
            return True
       return False

with OpenFileDemo('test.txt') as f:
    f.write('Foo\n')

三、异常处理

当上下文管理器遇到异常时由__exit__方法处理。传给__exit__方法的三个参数以下:
exc_type:异常类(例如SyntaxError)。
exc_value:异常实例。有时会有参数传给异常构造方法,例如错误信息,可使用exc_value.args获取这些参数。
traceback:traceback对象。code

3、@contextmanger装饰器

@contextmanger装饰器是contextlib模块中的工具,它能够将包含yield的语句变成上下文管理器。
其中,yield以前的语句在__enter__方法中执行yield以后的语句在__exit__方法执行,yield后面的值是函数的返回值,绑定到实际调用的with中的as子句的目标变量上
如此能够避免编写一个类来实现上下文管理器协议。对象

实例:@contextmanger装饰器应用之计时器

import contextlib
import time

@contextlib.contextmanager
def timer():
    start=time.time()
    yield
    end=time.time()
    usedtime=end-start
    print('Running time was %r seconds'%usedtime)

with timer() as usedtime:
    time.sleep(1)

注意:一旦with块在调用timer出现异常时,抛出的异常会在timer函数中的yield表达式中再次抛出。若是timer函数没有处理异常的代码就会致使函数运行停止,系统处于无效状态。所以必要时在上下文管理器函数中使用try/finally语句防范错误。

@contextmanger集合了三个不一样的Python特性:函数装饰器、生成器和with语句,很是实用!

4、contextlib模块中的实用工具

最后说明contextlib模块中包含的实用工具:

  • closing: 若是对象提供了 close() 方法,但没有实现 _enter__/__exit_ 协议,那么可使用这个函数构建上下文管理器。
  • suppress: 构建临时忽略指定异常的上下文管理器。
  • @contextmanager: 这个装饰器把简单的生成器函数变成上下文管理器,这样就不用建立类去实现管理器协议了。
  • ContextDecorator: 这是个基类,用于定义基于类的上下文管理器。这种上下文管理器也能用于装饰函数,在受管理的上下文中运行整个函数。
  • ExitStack: 这个上下文管理器能进入多个上下文管理器。with 块结束时,ExitStack 按照后进先出的顺序调用栈中各个上下文管理器的_exit_ 方法。若是事先不知道 with 块要进入多少个上下文管理器,可使用这个类。例如,同时打开任意一个文件列表中的全部文件。

显然,在这些实用工具中,使用最普遍的是 @contextmanager 装饰器,所以要格外留心。这个装饰器也有迷惑人的一面,由于它与迭代无关,却要使用 yield 语句。

相关文章
相关标签/搜索