python 上下文管理器及自定义原理解析


Posted in Python onNovember 19, 2019

这篇文章主要介绍了python 上下文管理器原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

Python 提供了 with 语法用于简化资源操作的后续清除操作,是 try/finally 的替代方法,实现原理建立在上下文管理器之上。

Python 提供了一个 contextmanager 装饰器,更进一步简化上下管理器的实现方式。

上下文管理器是Python2.5之后才出现的概念。上下文管理器规定了某个对象的使用范围,当进入或者离开了使用范围,都会有相应的一些调用,比如代码块开始时执行一些准备,代码块结束时结束一些操作。它更多的是用于资源的分配和释放上,即在开始时分配资源,结束时释放一些资源。比如在执行数据库查询时要建立连接,查询结束后要释放连接;写文件时要先打开文件,写结束后,要关闭文件等等。还有,就是资源的加锁和解锁,比如在使用多线程时,可能会用到加锁和解锁。

上下文管理器可以通过使用更可读、更精简的代码实现资源的分配与释放。
复制代码

with的使用

对于上下文管理器的使用,最常见的是使用with语句,with语句可构建资源的分配与释放的语法糖。

因为with语句就是为支持上下文管理器而存在的,使用上下文管理协议的方法包裹一个代码块(with语句体)的执行,并为try...except...finally提供了一个方便使用的封装。

一般语法:

def load_data(filename):
  f = file(filename,'w')
  try:
   f.write('test file')
  finally:
   f.close()

使用with:

# 使用with
with open('test.txt', 'w') as f:
  f.write('Python')

通过 with 语句在编写代码时,会使代码变得更加简洁,不用再去关闭文件。

我们并不需要写文件的关闭操作,文件会在使用完后自动关闭。

with的执行原理

实际上,在文件操作时,并不是不需要写文件的关闭,而是文件的关闭操作在 with 的上下文管理器中的协议方法里已经写好了。当文件操作执行完成后, with语句会自动调用上下文管理器里的关闭语句来关闭文件资源。

上下文管理协议(context management protocol)
ContextManager ,上下文是 context 直译的叫法,在程序中用来表示代码执行过程中所处的前后环境。

上下文管理器中有 __enter__ 和 __exit__ 两个方法,以with为例子,__enter__ 方法会在执行 with 后面的语句时执行,一般用来处理操作前的内容。比如一些创建对象,初始化等;__exit__ 方法会在 with 内的代码执行完毕后执行,一般用来处理一些善后收尾工作,比如文件的关闭,数据库的关闭等。

上下文管理协议包括两个方法:

contextmanager.__enter__() 从该方法进入运行时上下文,并返回当前对象或者与运行时上下文相关的其他对象。如果with语句有as关键词存在,返回值会绑定在as后的变量上。

contextmanager.__exit__(exc_type, exc_val, exc_tb) 退出运行时上下文,并返回一个布尔值标示是否有需要处理的异常。如果在执行with语句体时发生异常,那退出时参数会包括异常类型、异常值、异常追踪信息,否则,3个参数都是None。

with语句的语法如下:

with EXPR as VAR:
  BLOCK

with和as是关键词,EXPR就是上下文表达式,是任意表达式(一个表达式,不是表达式列表),VAR是赋值的目标变量。"as VAR"是可选的。

上述语句的底层实现可以这样描述:

mgr = (EXPR)
exit = type(mgr).__exit__ # 并没有调用
value = type(mgr).__enter__(mgr)
exc = True
try:
  try:
    VAR = value # 如果有"as VAR"
    BLOCK
  except:
    # 这里会处理异常
    exc = False
    if not exit(mgr, *sys.exc_info()):
      raise
    # 如果__exit__返回值是false,异常将被传播;如果返回值是真,异常将被终止
finally:
  if exc:
    exit(mgr, None, None, None)

这样with语句的执行过程就很清楚了。

  • 执行上下文表达式,获取上下文管理器
  • 加载上下文管理器的__exit__()方法以备后期调用
  • 调用上下文管理器的__enter__()方法
  • 如果with语句有指定目标变量,将从__enter__()方法获取的相关对象赋值给目标变量
  • 执行with语句体
  • 调用上下文管理器的__exit__()方法,如果是with语句体造成的异常退出,那异常类型、异常值、异常追踪信息将被传给__exit__(),否则,3个参数都是None。

也可以将多个表达式组织在一起。

with A() as a, B() as b:
BLOCK

它等价于

with A() as a: with B() as b: BLOCK

注:多上下文表达式是从python 2.7开始支持的

自定义上下文管理器(模拟with打开文件)

要实现一个自定义的上下文管理器,肯定要实现两个方法,一是进入对象范围时的准备工作,二是离开对象范围时的结束工作。

Python提供了两个类的方法分别实现上述功能:

  • __enter__ 进入对象范围时(一般代码块开始)被调用;
  • __exit__ 离开对象范围时(代码块结束)呗调用;

因此,一个Python类,只要实现了上述两种方法,就可以说是一个上下文管理器。

class MyOpen(object):
  def __init__(self,path,mode):
    # 记录要操作的文件路径和模式
    self.__path = path
    self.__mode = mode
 
  def __enter__(self):
    print('代码执行到了__enter__......')
    # 打开文件
    self.__handle = open(self.__path,self.__mode)
    # 返回打开的文件对象引用, 用来给 as 后的变量f赋值
    return self.__handle
 
  # 退出方法中,用来实现善后处理工作
  def __exit__(self, exc_type, exc_val, exc_tb):
    print('代码执行到了__exit__......')   
    self.__handle.close()
 
# a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
with MyOpen('test.txt','a+') as f:
  # 创建写入文件
  f.write("Hello Python!!!")
  print("文件写入成功")

通过执行顺序,可以看到文件写入操作执行完之后,自动调用了__exit__方法,做了善后处理工作。

代码执行到了__enter__......
文件写入成功
代码执行到了__exit__...... 
 

__exit__方法的参数

__exit__ 方法中有三个参数,用来接收处理异常,如果代码在运行时发生异常,异常会被保存到这里。

exc_type : 异常类型

exc_val : 异常值

exc_tb : 异常回溯追踪

# 编写两个数做除法的程序,然后给除数穿入0
class MyCount(object):
  # 接收两个参数
  def __init__(self,x, y):
    self.__x = x
    self.__y = y
  # 返回一个地址(实质是被as后的变量接收),实例对象就会执行MyCount中的方法:div()
  def __enter__(self):
    print('代码执行到了__enter__......')
    return self
  def __exit__(self, exc_type, exc_val, exc_tb):
    print("代码执行到了__exit__......")
    if exc_type == None:
      print('程序没问题')
    else:
      print('程序有问题,如果你能你看懂,问题如下:')
      print('Type: ', exc_type)
      print('Value:', exc_val)
      print('TreacBack:', exc_tb)
 
    # 返回值决定了捕获的异常是否继续向外抛出
    # 如果是 False 那么就会继续向外抛出,程序会看到系统提示的异常信息
    # 如果是 True 不会向外抛出,程序看不到系统提示信息,只能看到else中的输出
    return True
 
  def div(self):
    print("代码执行到了除法div")
    return self.__x / self.__y
with MyCount(1, 0) as mc:
  mc.div()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python中的MongoDB基本操作:连接、查询实例
Feb 13 Python
Python的Bottle框架中获取制定cookie的教程
Apr 24 Python
python实现各进制转换的总结大全
Jun 18 Python
Python编写Windows Service服务程序
Jan 04 Python
对Python3 解析html的几种操作方式小结
Feb 16 Python
Python二维码生成识别实例详解
Jul 16 Python
Python流程控制 if else实现解析
Sep 02 Python
python实现数据清洗(缺失值与异常值处理)
Dec 02 Python
python中with语句结合上下文管理器操作详解
Dec 19 Python
Python数据可视化处理库PyEcharts柱状图,饼图,线性图,词云图常用实例详解
Feb 10 Python
详解Windows下PyCharm安装Numpy包及无法安装问题解决方案
Jun 18 Python
Django cookie和session的应用场景及如何使用
Apr 29 Python
浅析python中while循环和for循环
Nov 19 #Python
django实现web接口 python3模拟Post请求方式
Nov 19 #Python
wxPython+Matplotlib绘制折线图表
Nov 19 #Python
python元组的概念知识点
Nov 19 #Python
python数值基础知识浅析
Nov 19 #Python
基于python实现蓝牙通信代码实例
Nov 19 #Python
使用IDLE的Python shell窗口实例详解
Nov 19 #Python
You might like
Blitz templates 最快的PHP模板引擎
2010/04/06 PHP
codeigniter数据库操作函数汇总
2014/06/12 PHP
php采用session实现防止页面重复刷新
2015/12/24 PHP
php获取目录中所有文件名及判断文件与目录的简单方法
2017/03/04 PHP
php 判断页面或图片是否经过gzip压缩的方法
2017/04/05 PHP
js中对象的声明方式以及数组的一些用法示例
2013/12/11 Javascript
JavaScript中的object转换成number或string规则介绍
2014/12/31 Javascript
JQuery中DOM事件绑定用法详解
2015/06/13 Javascript
详解JavaScript逻辑And运算符
2015/12/04 Javascript
javascript js 操作数组 增删改查的简单实现
2016/06/20 Javascript
jQuery插件JWPlayer视频播放器用法实例分析
2017/01/11 Javascript
JS得到当前时间的方法示例
2017/03/24 Javascript
JavaScript数据结构中栈的应用之表达式求值问题详解
2017/04/11 Javascript
ReactNative短信验证码倒计时控件的实现代码
2017/07/20 Javascript
Vue2.0父子组件传递函数的教程详解
2017/10/16 Javascript
webpack配置导致字体图标无法显示的解决方法
2018/03/06 Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【矩形情况】
2018/12/13 Javascript
功能完善的小程序日历组件的实现
2020/03/31 Javascript
在Chrome DevTools中调试JavaScript的实现
2020/04/07 Javascript
Node.js API详解之 zlib模块用法分析
2020/05/19 Javascript
[01:33:59]真人秀《加油 DOTA》 第六期
2014/09/09 DOTA
python搭建简易服务器分析与实现
2012/12/15 Python
Python标准库之多进程(multiprocessing包)介绍
2014/11/25 Python
Python使用scrapy采集数据时为每个请求随机分配user-agent的方法
2015/04/08 Python
学习python的前途 python挣钱
2019/02/27 Python
浅谈python锁与死锁问题
2020/08/14 Python
汽车广告策划方案
2014/05/31 职场文书
咖啡店创业计划书
2014/08/15 职场文书
三严三实对照检查材料思想汇报
2014/09/28 职场文书
党的群众路线教育实践活动个人整改措施
2014/10/27 职场文书
台风停课通知
2015/04/24 职场文书
迎新晚会主持词开场白
2015/05/28 职场文书
小学运动会加油稿
2015/07/22 职场文书
老舍《猫》教学反思
2016/02/17 职场文书
python3 删除所有自定义变量的操作
2021/04/08 Python
Pytest实现setup和teardown的详细使用详解
2021/04/17 Python