with 这个关键字,对于每一学习Python的人,都不会陌生。

操作文本对象的时候,几乎所有的人都会让我们要用 with open ,这就是一个上下文管理的例子。你一定已经相当熟悉了,我就不再废话了。

with open('test.txt') as f:

print f.readlines()

1. what context manager?

基本语法

with EXPR as VAR:

BLOCK

先理清几个概念

1. 上下文表达式:with open('test.txt') as f:

2. 上下文管理器:open('test.txt')

3. f 不是上下文管理器,应该是资源对象。

2. how context manager?

要自己实现这样一个上下文管理,要先知道上下文管理协议。

简单点说,就是在一个类里,实现了__enter__和__exit__的方法,这个类的实例就是一个上下文管理器。

例如这个示例:

class Resource():

def __enter__(self):

print('===connect to resource===')

return self

def __exit__(self, exc_type, exc_val, exc_tb):

print('===close resource connection===')

def operate(self):

print('===in operation===')

with Resource() as res:

res.operate()

我们执行一下,通过日志的打印顺序。可以知道其执行过程。

===connect to resource===

===in operation===

===close resource connection===

从这个示例可以很明显的看出,在编写代码时,可以将资源的连接或者获取放在__enter__中,而将资源的关闭写在__exit__ 中。

3. why context manager?

学习时多问自己几个为什么,养成对一些细节的思考,有助于加深对知识点的理解。

为什么要使用上下文管理器?

在我看来,这和 Python 崇尚的优雅风格有关。

可以以一种更加优雅的方式,操作(创建/获取/释放)资源,如文件操作、数据库连接;

可以以一种更加优雅的方式,处理异常;

第一种,我们上面已经以资源的连接为例讲过了。

而第二种,会被大多数人所忽略。这里会重点讲一下。

大家都知道,处理异常,通常都是使用 try...execept.. 来捕获处理的。这样做一个不好的地方是,在代码的主逻辑里,会有大量的异常处理代理,这会很大的影响我们的可读性。

好一点的做法呢,可以使用 with 将异常的处理隐藏起来。

仍然是以上面的代码为例,我们将1/0 这个一定会抛出异常的代码写在 operate 里

class Resource():

def __enter__(self):

print('===connect to resource===')

return self

def __exit__(self, exc_type, exc_val, exc_tb):

print('===close resource connection===')

return True

def operate(self):

1/0

with Resource() as res:

res.operate()

运行一下,惊奇地发现,居然不会报错。

这就是上下文管理协议的一个强大之处,异常可以在__exit__ 进行捕获并由你自己决定如何处理,是抛出呢还是在这里就解决了。在__exit__ 里返回 True(没有return 就默认为 return False),就相当于告诉 Python解释器,这个异常我们已经捕获了,不需要再往外抛了。

在 写__exit__ 函数时,需要注意的事,它必须要有这三个参数:

exc_type:异常类型

exc_val:异常值

exc_tb:异常的错误栈信息

当主逻辑代码没有报异常时,这三个参数将都为None。

4. how contextlib?

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

这个点Python早就想到了。它给我们提供了一个装饰器,你只要按照它的代码协议来实现函数内容,就可以将这个函数对象变成一个上下文管理器。

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

import contextlib

@contextlib.contextmanager

def open_func(file_name):

# __enter__方法

print('open file:', file_name, 'in __enter__')

file_handler = open(file_name, 'r')

# 【重点】:yield

yield file_handler

# __exit__方法

print('close file:', file_name, 'in __exit__')

file_handler.close()

return

with open_func('/Users/MING/mytest.txt') as file_in:

for line in file_in:

print(line)

在被装饰函数里,必须是一个生成器(带有yield),而yield之前的代码,就相当于__enter__里的内容。yield 之后的代码,就相当于__exit__ 里的内容。

上面这段代码只能实现上下文管理器的第一个目的(管理资源),并不能实现第二个目的(处理异常)。

如果要处理异常,可以改成下面这个样子。

import contextlib

@contextlib.contextmanager

def open_func(file_name):

# __enter__方法

print('open file:', file_name, 'in __enter__')

file_handler = open(file_name, 'r')

try:

yield file_handler

except Exception as exc:

# deal with exception

print('the exception was thrown')

finally:

print('close file:', file_name, 'in __exit__')

file_handler.close()

return

with open_func('/Users/MING/mytest.txt') as file_in:

for line in file_in:

1/0

print(line)

好像只要讲到上下文管理器,大多数人都会谈到打开文件这个经典的例子。

但是在实际开发中,可以使用到上下文管理器的例子也不少。我这边举个我自己的例子。

在OpenStack中,给一个虚拟机创建快照时,需要先创建一个临时文件夹,来存放这个本地快照镜像,等到本地快照镜像创建完成后,再将这个镜像上传到Glance。然后删除这个临时目录。

这段代码的主逻辑是创建快照,而创建临时目录,属于前置条件,删除临时目录,是收尾工作。

虽然代码量很少,逻辑也不复杂,但是“创建临时目录,使用完后再删除临时目录”这个功能,在一个项目中很多地方都需要用到,如果可以将这段逻辑处理写成一个工具函数作为一个上下文管理器,那代码的复用率也大大提高。

代码是这样的

总结起来,使用上下文管理器有三个好处:

提高代码的复用率;

提高代码的优雅度;

提高代码的可读性;

推荐下我本人原创的 《PyCharm 中文指南》电子书,内含大量(300张)的图解,制作之精良,值得每个 Python 工程师点个收藏。

python上下文管理关键字_详解 Python 中的 with 与 上下文管理器相关推荐

  1. python变量定义大全_详解python变量与数据类型

    这篇文章我们学习 Python 变量与数据类型 变量 变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念,变量可以通过变量名访问.在 Python 中 变量命名规定,必须是大小写英文,数字 ...

  2. python模块搜索原则_详解python模块路径查找规则及定义

    在python中创建一个模块非常简单,只需要在当前目录下创建一个 .py文件即可,系统自动将其解析成模块,文件名就是模块名.很多我们源码时代的同学都认为在查找模块时优先使用当前目录下的自定义模块.其实 ...

  3. python提取邮件附件_详解python实现读取邮件数据并下载附件的实例

    详解python实现读取邮件数据并下载附件的实例 实现结果图: 实现代码: #!/usr/bin/python2.7 # _*_ coding: utf-8 _*_ """ ...

  4. python停止线程池_详解python中Threadpool线程池任务终止示例代码

    需求 加入我们需要处理一串个位数(0~9),奇数时需要循环打印它:偶数则等待对应时长并完成所有任务:0则是错误,但不需要终止任务,可以自定义一些处理. 关键点 定义func函数处理需求 callbac ...

  5. python提取hbase数据_详解python操作hbase数据的方法介绍

    配置 thrift python使用的包 thrift 个人使用的python 编译器是pycharm community edition. 在工程中设置中,找到project interpreter ...

  6. python多线程操作列表_详解Python多线程下的list

    list 是 Python 常用的几个基本数据类型之一.正常情况下我们会对 list 有增删改查的操作,显然易见不会有任何问题.那么如果我们试着在多线程下操作list 会有问题吗? 多线程下的 lis ...

  7. python repair修复功能_详解Python修复遥感影像条带的两种方式

    GDAL修复Landsat ETM+影像条带 Landsat7 ETM+卫星影像由于卫星传感器故障,导致此后获取的影像出现了条带.如下图所示, 影像中均匀的布满条带. 使用GDAL修复影像条带的代码如 ...

  8. python导入模块介绍_详解Python模块导入方法

    python常被昵称为胶水语言,它能很轻松的把用其他语言制作的各种模块(尤其是C/C++)轻松联结在一起.python包含子目录中的模块方法比较简单,关键是能够在sys.path里面找到通向模块文件的 ...

  9. python换照片底色_详解Python给照片换底色(蓝底换红底)

    现在网上出现了很多在线换底色的网页版工具是这么做的呢?其实用Python就可以实现. 环境要求 Python3 numpy函数库 opencv库 安装 下载适应版本的numpy函数库,我电脑是WIN1 ...

最新文章

  1. Codeforces Round #545 B. Circus
  2. python有用吗-python有用么
  3. oracle jvm规范官网下载及相关书籍推荐
  4. 升级bigsur_升级 macOS Big Sur 后,程序监听端口报错
  5. 决策算法python_GitHub - nxety/MachineLearning_Python: 机器学习算法python实现
  6. 网络-console
  7. 9.判断回文数(力扣leetcode) 博主可答疑该问题
  8. Linux的vi命令使用详解
  9. 社交网络分析算法应用,社交网络分析算法
  10. 软件开发生命周期 --瀑布模型
  11. linux ext3 fsck一定要慎用
  12. 柯洁预言AI绘画会替代大部分画师,网友:初代AI受害者现身说法
  13. LSVGlobal Mapper应用----影像裁剪
  14. 企业微信批量操作工具1.0
  15. 【赛百味加入区块链试验项目以提高食品供应链透明度】GBCAX
  16. 神经网络量化----吐血总结
  17. 内网畅外网墙--再聊Nginx访问权限管理
  18. 隐私协议授权访问的实现
  19. h5跨域访问图片_网页保存为图片及高清截图的优化 | canvas跨域图片配置
  20. MySQL图形管理软件安装及使用

热门文章

  1. 只用html5与CSS做一个简单的页面,HTML+CSS基础训练之做一个简单页面的布局
  2. mysql 过程 的函数的区别是什么意思_Mysql中存储过程和函数的区别是什么
  3. 太阳光是平行光吗_阳光照耀,万物生长——神奇的太阳光,到底是什么?
  4. 学习Linux的一些建议
  5. oracle 等频直方图,等频直方图预估结果集行计算公式
  6. 容器注册到consul_Spring Cloud 系列之 Consul 注册中心(一)
  7. 自定义图框_Smart3D自定义图纸属性及其应用
  8. 如何在树莓派上进行python编程_《树莓派Python编程指南》怎么样_目录_pdf在线阅读 - 课课家教育...
  9. java判断对象无数据_java利用反射机制判断对象的属性是否为空以及获取和设置该属性的值...
  10. linux 修改ssh banner