第十五章 - 上下文管理器和else块

上下文管理器和else块

在使用Python编程中,可以会经常碰到这种情况:有一个特殊的语句块,在执行这个语句块之前需要先执行一些准备动作;当语句块执行完成后,需要继续执行一些收尾动作。

例如:当需要操作文件或数据库的时候,首先需要获取文件句柄或者数据库连接对象,当执行完相应的操作后,需要执行释放文件句柄或者关闭数据库连接的动作。

又如,当多线程程序需要访问临界资源的时候,线程首先需要获取互斥锁,当执行完成并准备退出临界区的时候,需要释放互斥锁。

对于这些情况,Python中提供了上下文管理器(Context Manager)的概念,可以通过上下文管理器来定义/控制代码块执行前的准备动作,以及执行后的收尾动作。

15.1 先做这个,再做那个:if语句之外的else块

else子句的行为如下:

for

  仅当for循环运行完毕时(没有被break终止),才运行else块。

while

  仅当while循环因为条件为假而退出时(没有被break终止),才运行else块。

try

  仅当try块中没有异常抛出时才运行else块。

 

15.2 上下文管理器和with块

上下文管理器对象存在的目的是管理with语句,就像迭代器的存在是为了管理for语句。with语句的目的是检查try/finally模式。

上下文管理器协议包含 __enter__ 和 __exit__ 两个方法,with语句在开始运行时会在上下文管理器对象上调用 __enter__方法,with语句运行结束后,会在上下文管理器对象上调用 __exit__ 方法。

 

示例15-2 Looking上下文管理器类的代码

class Looking(object):
    def __enter__(self):
        import sys
        self.original_write = sys.stdout.write
        sys.stdout.write = self.reverse_write  # 为sys.stdout.write打猴子补丁,替换成自己编写的方法
        return "JACK"

    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  # 如果__exit__方法返回None,或者True之外的值,with块中的任何异常都会向上冒泡


with Looking() as what:
    print("Alice, Kitty and Snowdrop")
    print(what)

>>>>
pordwonS dna yttiK ,ecilA
KCAJ

print(what)
>>>>
JACK

 

15.3 contextlib模块中的实用工具

closing, suppress, ContextDecorator, ExitStack,   @contextmanager

使用最广泛的是 @contextmanager装饰器,这个装饰器也有迷惑人的一面,因为它与迭代无关,却要使用yield语句。

 

15.4 使用@contextmanager

@contextmanager装饰器能减少创建上下文管理器的样板代码量,因为不用编写一个完整的类(定义 __enter__ __exit__方法),只需要实现由一个yield语句的生成器,生成想让__enter__方法返回的值。

在使用@contextmanager装饰的生成器中,yield语句的作用是把函数的定义体分成两部分:yield语句前面的所有代码在with块开始时(即调用__enter__方法时)执行,yield语句后面的代码在with块结束时(即调用__exit__方法时)执行。

 

示例15-4 使用生成器实现的上下文管理器

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

import contextlib

@contextlib.contextmanager
def looking():
    import sys
    original_write = sys.stdout.write

    def reverse_write(text):
        original_write(text[::-1])

    sys.stdout.write = reverse_write
    yield "JACK"
    sys.stdout.write = original_write

with looking() as what:
    print("Alex, Jack, Kitty")
    print(what)
>>>>
yttiK ,kcaJ ,xelA
KCAJ

print(what)
>>>>
JACK

@contextmanager装饰器优雅且实用,把三个不同的Python特性结合到了一起:函数装饰器、生成器、with语句。

 

posted @ 2017-07-27 23:20  Vincen_shen  阅读(113)  评论(0)    收藏  举报