Python with上下文管理器

上下文管理器

在使用Python编程中,可以会经常碰到这种情况:

有一个特殊的语句块,在执行这个语句块之前需要先执行一些准备动作;当语句块执行完成后,需要继续执行一些收尾动作。

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

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

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

with语法:
with context_expr [as var]:
    with_suite
  1. context_expr是支持上下文管理协议的对象,也就是上下文管理器对象,负责维护上下文环境
  2. as var是一个可选部分,通过变量方式保存上下文管理器对象
  3. with_suite就是需要放在上下文环境中执行的语句块
sample:
# with语句会在执行完代码块后自动关闭文件
with open('test.txt','w') as f:
    f.write('hello!')

自定义上下文管理器对象

对于自定义的类型,可以通过实现__enter____exit__方法来实现上下文管理器:

  • __enter__(self)
    • 在语句体执行之前进入运行时上下文
    • 如果有as子句,with语句将该方法的返回值赋值给 as 子句中的 target
    • 一般用于资源分配,如打开文件、连接数据库、获取线程锁
  • __exit__(self, exc_type, exc_val, exc_tb)
    • 在语句体执行完后从运行时上下文退出
    • exc_type判断是否有异常
    • 一般用于资源释放,如关闭文件、关闭数据库连接、释放线程锁
class MyContextManager():
    def __init__(self, a_list):
        print('初始化数据')
        self.a_list = a_list

    # 进入上下环境自动执行,并赋值as语句
    def __enter__(self):
        print('开始操作数据')
        self.a_list.append('python')
        return self

    # 退出上下文环境自动执行, 回收资源
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.a_list = None
        # 异常触发了
        if exc_type:
            print('发生异常')
        else:
            print('正常退出')

    def work(self):
        for item in self.a_list:
            print(item)


with MyContextManager(['a', 'b', 'c', 'd']) as f:
    f.work()

输出信息:

初始化数据
开始操作数据
a
b
c
d
python
正常退出

发生异常:

class MyContextManager():
    def __init__(self, a_list):
        print('初始化数据')

    # 进入上下环境自动执行
    def __enter__(self):
        print('开始操作数据')

    # 退出上下文环境自动执行, 回收资源
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.a_list = None
        # 异常触发了
        if exc_type:
            print(exc_type) # <class 'ZeroDivisionError'>
            print(exc_val) # division by zero
            print(exc_tb) # <traceback object at 0x000000000C1D0508>


with MyContextManager(['a', 'b', 'c', 'd']):
    1 / 0

contextlib

  • contextlib模块的contextmanager装饰器可以更方便的实现上下文管理器。

  • 任何能够被yield关键词分割成两部分的函数,都能够通过装饰器装饰的上下文管理器来实现。

    • 任何在yield之前的内容都可以看做在代码块执行前的操作,
    • 而任何yield之后的操作都可以放在exit函数中
  • yield返回的值相当于__enter__的返回值。

  • 当出现异常时,yield后的语句是不会执行的,想要异常安全,可用try捕捉异常

from contextlib import contextmanager


class MyContextManger():
    def __init__(self, a_list):
        print('初始化数据')
        self.a_list = a_list

    def work(self):
        for item in self.a_list:
            print(item)


@contextmanager  # 管理器对象
def mk_context(a_list):
    tls = MyContextManger(a_list)
    print('开始处理数据')

    try:
        yield tls  # 抛出来的实例对象
    except RuntimeError:
        pass

    print('处理完毕')


with mk_context(['a', 'b', 'c']) as mk:
    mk.work()

输出信息:

初始化数据
开始处理数据
a
b
c
处理完毕

closing

动触发执行内部close(自定义类,写一个close方法)

from contextlib import closing


class MyClass():
    def __init__(self):
        print('初始化数据')

    def work(self):
        print('执行数据')

    def close(self):
        print('关闭数据资源')


with closing(MyClass()) as f:
    f.work()

输出信息:

初始化数据
执行数据
关闭数据资源

猜你喜欢

转载自blog.csdn.net/qq_14876133/article/details/82289649