我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈
入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈
虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈
PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)文章合集 👈👈
Oracle数据库教程:👉👉 Oracle数据库文章合集 👈👈
优 质 资 源 下 载 :👉👉 资源下载合集 👈👈
自定义上下文管理器
- 自定义上下文管理器
- contextlib模块
- @contextlib.contextmanager装饰器
- contextlib.closing函数
- contextlib.nested函数(Python3.x 已弃用)
自定义上下文管理器
- 实际上就是创建一个类,实现
__enter__()
(进入)方法和__exit__()
(退出)方法 - 在
__enter__()
(进入)方法中执行一些准备操作 __exit__()
(退出)方法会接收到语句体执行时抛出的异常类型、异常的值和追踪信息- 示例
# 自定义上下文管理器 class Test: def __enter__(self): print('enter方法') return 'enter方法的返回值' def __exit__(self, exc_type, exc_val, exc_tb): """ exc_type:异常类型 exc_val:异常的值 exc_tb:追踪信息对象Traceback """ print('exit方法', exc_type, exc_val, exc_tb) # 使用自定义上下文管理器 with Test() as x: print(x) 1 / 0 # ============输出结果============ # enter方法 # enter方法的返回值 # exit方法 <class 'ZeroDivisionError'> division by zero <traceback object at 0x000002009555F400> # Traceback (most recent call last): # File "E:\StudyCode\Python\14-异常处理\03-自定义上下文管理器.py", line 12, in <module> # 1 / 0 # ZeroDivisionError: division by zero
- 示例解析
# with 后面的 Test() 是创建一个类实例对象-上下文管理器 # 自动执行上下文管理器对象内部的 __enter__() 方法 # 比如文件操作的 open(),就可以把文件打开操作放到 __enter__() 方法内部 # 执行完 __enter__() 方法,会把这个方法的返回值赋值给 with 语句中 as 后面的变量 x # 执行语句体: print(x) 和 1 / 0 # 最后自动执行上下文管理器对象内部的 __exit__() 方法,这个方法会接收到语句体抛出的异常(包含:异常类型、异常值和追踪信息对象) # __exit__() 方法的返回值为 True 则不会再将接收到的异常往外抛出,如果返回值为 False 则将接收到的异常继续往外抛出 # 所有最后的清理操作都可以写到 __exit__() 方法中(比如文件操作中最后的关闭文件操作)
- 在
__exit__()
方法中接收到的追踪信息对象,我们也可以获取到对象的相关信息# 自定义上下文管理器 class Test: def __enter__(self): # print('enter方法') return 'enter方法的返回值' def __exit__(self, exc_type, exc_val, exc_tb): """ exc_type:异常类型 exc_val:异常的值 exc_tb:追踪信息对象 """ # print('exit方法', exc_type, exc_val, exc_tb) # 获取追踪信息对象的内容 import traceback print(traceback.extract_tb(exc_tb)) return True # 使用自定义上下文管理器 with Test() as x: # print(x) 1 / 0 # ========== 输出结果 ========== # [<FrameSummary file E:\StudyCode\Python\14-异常处理\03-自定义上下文管理器.py, line 22 in <module>>]
contextlib模块
@contextlib.contextmanager装饰器
- 作用
- 使用装饰器,让生成器变成上下文管理器
- 知识回顾
- 生成器:包含 yield 语句的函数,这个函数的执行结果就是“生成器”
- 关于生成器
def Test(): print('Text函数', 1) yield '返回值1' print('Text函数', 2)
- 使用装饰器,让生成器变成上下文管理器
import contextlib @contextlib.contextmanager def Test(): print('Text函数', 1) yield '返回值1' print('Text函数', 2) with Test() as x: 2 / 1 print(x) # ========== 输出结果 ========== # Text函数 1 # 返回值1 # Text函数 2
- 执行分解
- 使用
@contextlib.contextmanager
装饰之后 yield
之前的语句就是上下文管理器中的__enter__
方法yield
语句就是执行 语句体yield
之后的语句就是上下文管理器中的__exit__
方法
- 使用
- 应用示例
import contextlib @contextlib.contextmanager def Test(): try: yield except Exception as e: print('异常:', e) with Test() as x: 2 / 0 # ========== 输出结果 ========== 异常: division by zero # 步骤解析 # try语句写在yield之前,会先执行 # 通过yield语句执行语句体 2/0,会抛出异常 # 执行yield后面的except语句捕获异常
contextlib.closing函数
- 作用
- 使用函数,让拥有
close()
方法但不是上下文管理器的对象变成上下文管理器 - 注意:这个对象中必须要有
close()
方法,而且是要求最后必须执行close()
方法
- 使用函数,让拥有
- 通过案例讲解
- 有一个如下的类对象
class Test: def tt(self): print('tt方法') def close(self): print('对象关闭,释放资源')
- 使用需求:
Test
类实例对象每次调用完tt()
方法后,都需要执行close()
方法关闭对象,释放资源 - 按照我们刚学的上下文管理器进行操作,就需要再
Test
类当中增加__enter__()
和__exit__()
两个方法class Test: def tt(self): print('tt方法') def close(self): print('对象关闭,释放资源') def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.close() # 要求:Test类实例对象每次调用完tt()方法后,都需要执行close()方法关闭对象,释放资源 with Test() as x: x.tt() # ========== 输出结果 ========== # tt方法 # 对象关闭,释放资源 # 解析:为什么要在__enter__中返回一个self # 因为我们需要在with语句里面去执行实例方法tt(),所以我们需要拿到这个实例对象 # 通过__enter__方法使用self将这个实例对象返回,就可以将这个实例对象赋值给with语句中的x # 再通过 x 去调用实例方法 tt()
- 这样写的话,就每次都要添加
__enter__()
和__exit__()
两个方法,比较麻烦 - 我们可以通过
contextlib.closing()
函数来解决这个问题class Test: def tt(self): print('tt方法') def close(self): print('对象关闭,释放资源') # 要求:Test类实例对象每次调用完tt()方法后,都需要执行close()方法关闭对象,释放资源 import contextlib with contextlib.closing(Test()) as x: x.tt()
- 只需要在with语句中使用
contextlib.closing()
函数就能达到上面增加__enter__()
和__exit__()
两个方法的效果
- 有一个如下的类对象
contextlib.nested函数(Python3.x 已弃用)
contextlib.nested()
函数在Python3.x中已经已弃用,仅做了解即可!- 作用
- 在Python2.7版本之前,完成多个上下文管理器的嵌套
- 通过案例讲解
- 需求:读取一个文件所有内容,再把内容写入到另一个文件中
- 在Python2.7版本之前的写法
with open('xx.jpg', mode='rb') as form_file: with open('xx2.jpg',mode = 'wb') as to_file: file_content = form_file.read() to_file.write(file_content)
- 这样写的话,就需要用到多个
open()
对象的上下文管理器做嵌套 - 在Python2.7之前,就可以用
contextlib.nested()
函数来处理
import contextlib with contextlib.nested(open('xx.jpg', mode='rb'), open('xx2.jpg', mode='wb')) as (form_file,to_file): file_content = form_file.read() to_file.write(file_content)
- 但是,在Python3.x版本对这种多个上下文管理器的嵌套进行了优化,也不在需要用
contextlib.nested
函数了
with open('xx.jpg', mode='rb') as form_file, open('xx2.jpg', mode='wb') as to_file: file_content = form_file.read() to_file.write(file_content)
- 关于这个函数我们只需要了解一下就行,记住Python3.x版本的写法即可