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

第15章–上下文管理器和else块

15.1 else块

else语句不仅可以用于if后面,也可以用于for while try后
for
仅当 for 循环运行完毕时(即 for 循环没有被 break 语句中止)才运行 else 块。
while
仅当 while 循环因为条件为假值而退出时(即 while 循环没有被 break 语句中止)才运
行 else 块。
try
仅当 try 块中没有异常抛出时才运行 else 块。官方文档(https://docs.python.org/3/
reference/compound_stmts.html)还指出:“else 子句抛出的异常不会由前面的 except 子
句处理。”

15.2 上下文管理器和with块

上下文管理器对象存在的目的是管理with语句
with语句存在的目的是简化try/finally模式

上下文管理器对象是实现了__enter__和__exit__方法的类对象,enter在with语句开始时执行,exit在with语句结束或抛出异常时执行。
as 后的对象是enter返回的值,exit方法的传入参数存储了关于异常的信息,如果with语句中没有抛出异常,那么exit方法中的后三个参数都为None,如果在with语句中抛出了异常,异常的行为按exit方法返回的值是否为True可以分为两种:如果exit方法返回True,则异常不会向上冒泡,异常在with语句内就被处理了;如果exit方法返回不是True,则异常会向上冒泡,具体例子代码如下

无抛出异常,则exit方法的三个参数都为None

class A():

    def __enter__(self):
        print('A enter')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('A exit')
        print(exc_type,exc_val,exc_tb)
        return True

with A() as a:
    print(a)
# A enter
# <__main__.A object at 0x000001D94DE66518>
# A exit
# None None None

with内抛出异常,exit方法返回True,最终程序无报错

class A():

    def __enter__(self):
        print('A enter')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('A exit')
        print(exc_type,exc_val,exc_tb)
        return True

with A() as a:
    print(a)
    raise Exception()

# A enter
# <__main__.A object at 0x0000027B689364E0>
# A exit
# <class 'Exception'>  <traceback object at 0x0000027B68920788>
#
# Process finished with exit code 0

with内抛出异常,exit方法返回False,最终程序报错,exit code不为0

class B():

    def __enter__(self):
        print('B enter')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('B exit')
        print(exc_type,exc_val,exc_tb)
        return False

with B() as b:
    print(b)
    raise Exception()

# B enter
# <__main__.B object at 0x0000021FA308D470>
# B exit
# <class 'Exception'>  <traceback object at 0x0000021FA3070C08>
# Traceback (most recent call last):
#   File "test.py", line 36, in <module>
#     raise Exception()
# Exception
#
# Process finished with exit code 1

15.3 contextlib模块

https://docs.python.org/3/library/contextlib.html

This module provides utilities for common tasks involving the with statement
contextlib是官方的处理with模块相关任务的库

15.4 @contextmanager

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

如果with语句中抛出了异常,那么异常会传递到contextmanager修饰的生成器中的yield语句,如果在yield语句前后没有try except捕获异常,则程序会中断,导致yield语句后面的语句无法执行,不符合上下文管理器的概念。
所以使用 @contextmanager 装饰器时,要把 yield 语句放在 try/finally 语句中
(或者放在 with 语句中),这是无法避免的,因为我们永远不知道上下文管理器
的用户会在 with 块中做什么

yield不用try except捕获,"after yield"没有被打印出来

from contextlib import contextmanager

@contextmanager
def gen():
    print('before yield')
    yield 'hello'
    print('after yield')

with gen() as f:
    print(f)
    raise Exception()

# before yield
# hello
# Traceback (most recent call last):
#   File "test.py", line 11, in <module>
#     raise Exception()
# Exception
# 
# Process finished with exit code 1

yield用try except捕获,“after yield”被正常打印

from contextlib import contextmanager

@contextmanager
def gen():
    print('before yield')
    try:
        yield 'hello'
    except Exception:
        print('catch Exception')
    finally:
        print('after yield')

with gen() as f:
    print(f)
    raise Exception()

# before yield
# hello
# catch Exception
# after yield
发布了51 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_36267931/article/details/103029997