美文网首页
Python:上下文管理器 与 contextlib 的用法解析

Python:上下文管理器 与 contextlib 的用法解析

作者: dex0423 | 来源:发表于2020-07-21 19:16 被阅读0次

1. 上下文管理器

1.1. 引言

  • 在 python 语言中经常会使用 with ... as ... 语句,常见的 with open 语句就是其典型代表,如下:
with open('filename.csv') as file:
    print(file.readlines())
  • 我们之所以会使用上面的代码打开并操作文件,是因为它是一个上下文管理器、实现了上下文管理;
  • 那么上下文管理器到底是个什么东西,为什么要使用上下文管理器,接下来的内容将对此做出解答;

1.2. 什么是上下文管理器

  • 定义:
    -- 在一个类里,如果同时实现了__ enter __ 和 __ exit __ 的方法,这个类的实例就是一个上下文管理器,它可以使用 with 语句创建一个上下文管理器;
  • 示例:

>>> class Sample():
...     def __enter__(self):
...         print("get resource")                    # 打印 获取资源
...         return self                              # 注意:此处一定要返回 self 实例
...     def __exit__(self, exc_type, exc_val, exc_tb):
...         print("release resource")                # 打印释放资源
...     def do_job(self):
...         print("do job")
...
>>>
>>> with Sample() as sample:
...     sample.do_job()
...
get resource        # 获取资源
do job              # 执行任务
release resource    # 释放资源
>>>

-- 在上面的代码语句中:

with Sample() as sample: 是 上下文表达式
Sample() 是 上下文管理器
sample 是 资源对象

1.3. __ enter __ 的作用是什么

  • __ enter __ 用于 赋值 给 as 后面的变量;
  • 不过 with 语句中 as 不是必须的;

1.4. __ exit __ 的作用是什么

-- __ exit __ 用于 捕获异常,它的返回值是一个 boolean 对象;
-- 除了 self 之外,必须传入另外三个参数,分别表示 exception 的类型,值,以及 traceback;
-- 如果返回 True 则表示这个异常被忽略;
-- 如果返回 None,False 等则这个异常会抛出。

1.5. 上下文管理器的两个作用

  • 上下文管理器 两大作用:
    -- 可以以一种更加优雅的方式,操作(创建/获取/释放)资源,如文件操作、数据库连接;
    -- 可以以一种更加优雅的方式,处理异常
  • 作用一:操作资源

-- 还是以第一段代码为例:

with open('filename.csv') as file:
    print(file.readlines())

-- 操作文件流程:

1. open('filename.csv') 创建上下文管理器 > 2. 获取资源 file > 3. readlines() 阅读行 > 4. 释放资源 file

  • 作用二:处理异常
    -- 处理异常,主要是 __ exit __ 方法的,若在执行流程中因为错误而退出,调用 __ exit__ 时,会自动捕获错误信息;
    -- 如果 __ exit__ 最后返回 True,则表示异常被忽略,即使出现异常程序也会继续执行下去,而不会抛出异常中断程序,这将增加代码的健壮性;
>>> class Sample():
...     def __enter__(self):
...         return self
...     def __exit__(self, exc_type, exc_value, traceback):
...         """
...         若在执行流程中因为错误而退出,调用 exit 时,会自动捕获错误信息
...         exc_type: 错误的类型(异常类型)
...         exc_val: 错误类型对应的值 (异常值)
...         exc_tb: 代码中错误发生的位置 (错误栈)
...         """
...         if exc_type == IndexError:
...             print("执行出错 IndexError")
...             print(exc_value, type(exc_value))
...             print(traceback)
...             return True
...         elif exc_type == ZeroDivisionError:
...             print("执行出错 ZeroDivisionError")
...             print(exc_value, type(exc_value))
...             print(traceback)
...             return True
...     def do_wrong_job(self):
...         a = 1 / 0                       # 1 / 0 ,是一个错误的式子,应该会报错
...         return a
...
>>> with Sample() as sample:
...     sample.do_wrong_job()
...
执行出错 ZeroDivisionError
division by zero <class 'ZeroDivisionError'>
<traceback object at 0x00000212FDFFE208>

2. contextlib

2.1. 引言

  • 在上面的例子中,我们只是想构建一个上下文管理器,却写了一个复杂的类;
  • 如果只是要实现一个简单的功能,写一个类未免有点过于繁杂,如果只写一个函数就可以实现上下文管理器就好了,这就需要 contextlib 出场了;

2.2. 什么是 contextlib

  • contextlib 是一个装饰器,只要按照它的代码协议来实现函数内容,就可以将这个函数对象变成一个上下文管理器。

2.3. contextlib 实现上下文管理器

  • 我们按照 contextlib 的协议来自己实现一个打开文件(with open)的上下文管理器。
import contextlib


@contextlib.contextmanager
def open_func(file_name):
    # __enter__方法
    print('open file:', file_name)
    file_handler = open(file_name, 'r')
    
    yield file_handler          # 这里的 yield 用于返回一个生成器
    
    # __exit__方法
    print('close file:', file_name)
    file_handler.close()
    return


with open_func(r'filename') as file_in:
    for line in file_in:
        print(line)

2.4. contextlib 实现捕获异常


>>> import contextlib
>>>
>>> @contextlib.contextmanager
... def open_func(file_name):
...     # __enter__方法
...     print('open file:', file_name, 'in __enter__')
...     try:
...         file_handler = open(file_name, 'r', encoding="utf-8")
...         yield file_handler
...     except Exception as exc:
...         # deal with exception
...         print(f'the exception was thrown {exc}')
...     finally:
...         print('close file:', file_name, 'in __exit__')
...         try:
...             file_handler.close()
...         except:
...             return
...         return
...
>>> with open_func(r'filename') as file_in:
...     for line in file_in:
...         1/0
...         print(line)
...
open file: C:\Users\Administrator\Desktop\jiaohuobao\queried_merchants.csv in __enter__
the exception was thrown division by zero
close file: C:\Users\Administrator\Desktop\jiaohuobao\queried_merchants.csv in __exit__

相关文章

网友评论

      本文标题:Python:上下文管理器 与 contextlib 的用法解析

      本文链接:https://www.haomeiwen.com/subject/jpxwkktx.html