《流畅的Python》读书笔记18(第十五章:上下文管理器和else块)

79 阅读2分钟

本章讨论其他语言中不常见的一些流程控制特性,包括:

  • with语句和上下文管理器
  • for、while和try语句的else子句

with语句会设置一个临时上下文,交给上下文管理器对象控制,并且负责清理上下文。

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

else子句不仅能在if语句中使用,还能在for、while语句中使用。

15.2 上下文管理器和with块

with语句的目的是简化try/finally模式。

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

示例15-1 演示把文件对象当成上下文管理器使用

>>> with open('mirror.py') as fp:

...     src = fp.read(60)

... 

>>> len(src)

60

>>> fp

<_io.TextIOWrapper name='mirror.py' mode='r' encoding='UTF-8'>

>>> fp.closed, fp.encoding

(True, 'UTF-8')

>>> fp.read(60)

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

ValueError: I/O operation on closed file.

>>>

不管控制流程以哪种方式退出with块,都会在上下文管理器对象上调用__exit__方法,而不是在__enter__方法返回的对象上调用。

示例15-2 测试LookGlass上下文管理器类

>>> from mirror import LookingGlass

>>> with LookingGlass() as what:

...     print('Alice, Kitty and Snowdrop')

...     print(what)

... 

pordwonS dna yttiK ,ecilA

YKCOWREBBAJ

>>> what

'JABBERWOCKY'

>>> print('Back to normal')

Back to normal

示例15-3 mirror.py: LookingGlass上下文管理器类的代码

class LookingGlass:
    def __enter__(self):
        import sys
        self.original_write = sys.stdout.write
        sys.stdout.write = self.reverse_write
        return 'JABBERWOCKY'

    def reverse_write(self, text):
        self.original_write(text[::-1])

    def __exit__(self, exc_type, exc_value, traceback):
        import sys
        sys.stdout.write = self.original_write
        if exc_type is ZeroDivisionError:
            print('Please DO NOT divide by zero!')
            return True

示例15-4 在with块之外使用LookingGlass类

>>> from mirror import LookingGlass

>>> manager = LookingGlass()

>>> manager

<mirror.LookingGlass object at 0x7f7fc82bc9e8>

>>> monster = manager.__enter__()

>>> monster == 'JABBERWOCKY'

eurT

>>> monster

'YKCOWREBBAJ'

>>> manager

>8e9cb28cf7f7x0 ta tcejbo ssalGgnikooL.rorrim<

>>> manager.__exit__(None, None, None)

>>> monster

'JABBERWOCKY'

15.3 contextlib模块中的实用工具

docs.python.org/3/library/c…

15.4 使用@contextmanager

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

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

示例15-5 mirror_gen.py:使用生成器实现的上下文管理器

import contextlib


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

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

    sys.stdout.write = reverse_write
    yield 'JABBERWOCKY'
    sys.stdout.write = original_write

示例15-6 测试looking_glass上下文管理器函数

>>> from mirror_gen import looking_glass

>>> with looking_glass() as what:

...     print('Alice, Kitty and Snowdrop')

...     print(what)

... 

pordwonS dna yttiK ,ecilA

YKCOWREBBAJ

>>> what

'JABBERWOCKY'

注意,在@contextmanager装饰器的生成器中,yield与迭代没有任何关系。