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__