详解python with 上下文管理器


Posted in Python onSeptember 02, 2020

作为一个 Java 为母语的程序员来讲,学习起其他新的语言就难免任何事都与 Java 进行横向对比。Java 7 引入了能省去许多重复代码的 try-with-resources 特性,不用每回 try/finally 来释放资源(不便之处有局部变量必须声明在 try 之前,finally 里还要嵌套 try/catch 来处理异常)。比如下面的 Java 代码

try(InputStream inputStream = new FileInputStream("abc.txt")) {
  System.out.println(inputStream.read());
} catch (Exception ex) {
}

它相应的不使用 try-with-resources 语法的代码就是

InputStream inputStream = null;
try {
  inputStream = new FileInputStream("abc.txt");
} catch (Exception ex) {
} finally {
  if(inputStream != null) {
    try {
      inputStream.close();
    } catch (Exception ex) {
    }
  }
}

类似的 Python 也有自己的 try-with-resources 写法,就是 with 关键字,它的概念叫做上下文管理器(Context Manager)。

with 关键字的使用

with open('some_file', 'w') as opened_file:
  opened_file.write('Hola!')

以上的代码相当于

opened_file = open('some_file', 'w')
try:
  opened_file.write('Hola!')
finally:
  opened_file.close()

也就是 with 关键字打开的资源会在 with 语句块结束后自动调用相应的方法自动释放(无论 with 中操作是否有异常)。

with 用起来是很方便的,但是什么样的资源可以用 with 关键字?Python 是怎么知道要调用哪个方法来关闭资源的?进而如何实现自己的支持上下文管理器的 Python 类。

再次回顾 Java 的 try-with-resources 语法,try(...) 括号支持的类必须是实现了 AutoCloseable 接口,它的接口方法是

public void close() throws IOException

也就是 Java 的 try-with-resources 语法会自动调用以上方法来释放资源,要实现可被自动释放的 Java 就只须遵照这一规则就行。

而在 Python 中,能被 with 的类有两种实现方式

实现基本方法以支持上下文管理器的类

一个 Python 类要能被用于 with 上下文,必须实现至少 __enter__ __exit__ 方法。这两个方法的意思好理解,一个是创建资源后,后者是退出 with 语句块后。请看下面的例子

class File(object):
  def __init__(self, file_name, method):
    self.file_obj = open(file_name, method)
 
  def __enter__(self):
    print("---enter")
    return self.file_obj
 
  def __exit__(self, type, value, traceback):
    print("---exit")
    self.file_obj.close()
 
 
with File('data.txt', 'r') as data_file:
  print(data_file.read())

假设 data.txt 文件中的内容是

hello
world

那么以上程序执行后的输出就是

--enter
hello
world
---exit

  1. __enter__ 返回的值作为 with ... as data_file 中的 data_file 变量的值,如果 __enter__ 没有返回,data_file 得到的就是 NoneType object 了。
  2. __exit__ 可利用来释放资源
  3. 没有 __enter__ 方法试图用 with 的写法执行时会得到 AttributeErro: __enter__ 异常
  4. 同样,没有 __exit__ 方法试图用 with 的写法执行时会得到 AttributeErro: __exit__ 异常
  5. __exit__ 有其他额外的三个参数,可获得资源的值,以及能处理 with 块中执行出现异常的情况
  6. __exit__ 的返回值也有用途,如果它返回 True 则出现的异常不再向外传播,其他值的话直接向外抛

利用生成器(Generator) 和装饰器创建支持上下文管理器的方法

此种方式比较简单,不过逻辑控制上没有这么强。

from contextlib import contextmanager
 
@contextmanager
def open_file(name, method):
  f = open(name, method)
  yield f
  f.close()

使用 f 的执行代码将被放置在 yield f 所处的位置,with 使用以上方法。yield 后的 f 变量将是 with...as 后的变量值

with open_file('some_file', 'w') as file_object:
  file_object.write('hola!')

这里也要注意异常处理的情况,比如把上面代码打开文件的模式换作 r, 仍然试图去写文件,这样在 open_file 方法的 yield f 位置将产生异常,会造成 f.close() 得不到执行,不能正确释放该资源。

欲更具防御性,前面的 yield f 可以扩展也如下的形式

try:
  yield f
except Exception as ex:
  pass #处理异常,或继续向外抛
finally:
  f.close()

@contextmanager 装饰器内部也是封装为一个实现了 __enter__ __exit__ 方法的对象。

参考链接:Context Managers

以上就是详解python with 上下文管理器的详细内容,更多关于python with 上下文管理器的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
研究Python的ORM框架中的SQLAlchemy库的映射关系
Apr 25 Python
举例讲解Python编程中对线程锁的使用
Jul 12 Python
python实现二叉查找树实例代码
Feb 08 Python
python使用Tesseract库识别验证
Mar 21 Python
python向已存在的excel中新增表,不覆盖原数据的实例
May 02 Python
详解Django 中是否使用时区的区别
Jun 14 Python
python将回车作为输入内容的实例
Jun 23 Python
在python中以相同顺序shuffle两个list的方法
Dec 13 Python
Python基于mysql实现学生管理系统
Feb 21 Python
python根据txt文本批量创建文件夹
Dec 08 Python
python调用pyaudio使用麦克风录制wav声音文件的教程
Jun 26 Python
Django模板获取field的verbose_name实例
May 19 Python
Python 的 __str__ 和 __repr__ 方法对比
Sep 02 #Python
Python datetime 如何处理时区信息
Sep 02 #Python
浅析python中的del用法
Sep 02 #Python
浅析NumPy 切片和索引
Sep 02 #Python
详解Python 函数参数的拆解
Sep 02 #Python
Python 常用日期处理 -- calendar 与 dateutil 模块的使用
Sep 02 #Python
python 常用日期处理-- datetime 模块的使用
Sep 02 #Python
You might like
PHP syntax error, unexpected $end 错误的一种原因及解决
2008/10/25 PHP
php设计模式之职责链模式实例分析【星际争霸游戏案例】
2020/03/27 PHP
JavaScript实现页面实时显示当前时间的简单实例
2013/07/20 Javascript
jQuery中:visible选择器用法实例
2014/12/30 Javascript
jquery.validate使用时遇到的问题
2015/05/25 Javascript
利用jQuery设计一个简单的web音乐播放器的实例分享
2016/03/08 Javascript
超漂亮的Bootstrap 富文本编辑器summernote
2016/04/05 Javascript
原生JS实现九宫格抽奖效果
2017/04/01 Javascript
js的函数的按值传递参数(实例讲解)
2017/11/16 Javascript
vue 实现全选全不选的示例代码
2018/03/29 Javascript
ES6入门教程之Array.from()方法
2019/03/23 Javascript
微信小程序添加插屏广告并设置显示频率(一天一次)
2019/12/06 Javascript
在Echarts图中给坐标轴加一个标识线markLine
2020/07/20 Javascript
[06:33]3.19 DOTA2发布会 海涛、冷冷、2009见证希望
2014/03/21 DOTA
Python __setattr__、 __getattr__、 __delattr__、__call__用法示例
2015/03/06 Python
python编码总结(编码类型、格式、转码)
2016/07/01 Python
Python竟能画这么漂亮的花,帅呆了(代码分享)
2017/11/15 Python
python logging模块书写日志以及日志分割详解
2019/07/22 Python
Python数据可视化 pyecharts实现各种统计图表过程详解
2019/08/15 Python
解决python彩色螺旋线绘制引发的问题
2019/11/23 Python
Tensorflow 定义变量,函数,数值计算等名字的更新方式
2020/02/10 Python
TensorFlow的环境配置与安装教程详解(win10+GeForce GTX1060+CUDA 9.0+cuDNN7.3+tensorflow-gpu 1.12.0+python3.5.5)
2020/06/22 Python
python3 中使用urllib问题以及urllib详解
2020/08/03 Python
Python获取android设备cpu和内存占用情况
2020/11/15 Python
涂鸦板简单实现 Html5编写属于自己的画画板
2016/07/05 HTML / CSS
canvas绘制文本内容自动换行的实现代码
2019/01/14 HTML / CSS
德国购买踏板车网站:Microscooter
2019/10/14 全球购物
旅游管理专业生自荐信范文
2014/01/02 职场文书
2014新年寄语
2014/01/20 职场文书
《藏戏》教学反思
2014/02/11 职场文书
洗发露广告词
2014/03/14 职场文书
新教师岗前培训方案
2014/06/05 职场文书
用人单位终止解除劳动合同证明书
2014/10/06 职场文书
单位作风建设剖析材料
2014/10/11 职场文书
2014年大学教师工作总结
2014/12/02 职场文书
2014年医院后勤工作总结
2014/12/06 职场文书