正确理解python中的关键字“with”与上下文管理器


Posted in Python onApril 21, 2017

前言

如果你有阅读源码的习惯,可能会看到一些优秀的代码经常出现带有 “with” 关键字的语句,它通常用在什么场景呢?今天就来说说 with 和 上下文管理器。

对于系统资源如文件、数据库连接、socket 而言,应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(断开)该资源。

比如 Python 程序打开一个文件,往文件中写内容,写完之后,就要关闭该文件,否则会出现什么情况呢?极端情况下会出现 "Too many open files" 的错误,因为系统允许你打开的最大文件数量是有限的。

同样,对于数据库,如果连接数过多而没有及时关闭的话,就可能会出现 "Can not connect to MySQL server Too many connections",因为数据库连接是一种非常昂贵的资源,不可能无限制的被创建。

来看看如何正确关闭一个文件。

普通版:

def m1():
 f = open("output.txt", "w")
 f.write("python之禅")
 f.close()

这样写有一个潜在的问题,如果在调用 write 的过程中,出现了异常进而导致后续代码无法继续执行,close 方法无法被正常调用,因此资源就会一直被该程序占用者释放。那么该如何改进代码呢?

进阶版:

def m2():
 f = open("output.txt", "w")
 try:
 f.write("python之禅")
 except IOError:
 print("oops error")
 finally:
 f.close()

改良版本的程序是对可能发生异常的代码处进行 try 捕获,使用 try/finally 语句,该语句表示如果在 try 代码块中程序出现了异常,后续代码就不再执行,而直接跳转到 except 代码块。而无论如何,finally 块的代码最终都会被执行。因此,只要把 close 放在 finally 代码中,文件就一定会关闭。

高级版:

def m3():
 with open("output.txt", "w") as f:
 f.write("Python之禅")

一种更加简洁、优雅的方式就是用 with 关键字。open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的。那么它的实现原理是什么?

在讲 with 的原理前要涉及到另外一个概念,就是上下文管理器(Context Manager)。

上下文管理器

任何实现了 __enter__() __exit__() 方法的对象都可称之为上下文管理器,上下文管理器对象可以使用 with 关键字。显然,文件(file)对象也实现了上下文管理器。

那么文件对象是如何实现这两个方法的呢?我们可以模拟实现一个自己的文件类,让该类实现 __enter__() __exit__() 方法。

class File():

 def __init__(self, filename, mode):
 self.filename = filename
 self.mode = mode

 def __enter__(self):
 print("entering")
 self.f = open(self.filename, self.mode)
 return self.f

 def __exit__(self, *args):
 print("will exit")
 self.f.close()

__enter__() 方法返回资源对象,这里就是你将要打开的那个文件对象, __exit__() 方法处理一些清除工作。

因为 File 类实现了上下文管理器,现在就可以使用 with 语句了。

with File('out.txt', 'w') as f:
 print("writing")
 f.write('hello, python')

这样,你就无需显示地调用 close 方法了,由系统自动去调用,哪怕中间遇到异常 close 方法也会被调用。

contextlib

Python 还提供了一个 contextmanager 的装饰器,更进一步简化了上下文管理器的实现方式。通过 yield 将函数分割成两部分,yield 之前的语句在 __enter__ 方法中执行,yield 之后的语句在 __exit__ 方法中执行。紧跟在 yield 后面的值是函数的返回值。

from contextlib import contextmanager

@contextmanager
def my_open(path, mode):
 f = open(path, mode)
 yield f
 f.close()

调用

with my_open('out.txt', 'w') as f:
 f.write("hello , the simplest context manager")

总结

Python 提供了 with 语法用于简化资源操作的后续清除操作,是 try/finally 的替代方法,实现原理建立在上下文管理器之上。此外,Python 还提供了一个 contextmanager 装饰器,更进一步简化上下管理器的实现方式。以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
Python中实现字符串类型与字典类型相互转换的方法
Aug 18 Python
通过Python使用saltstack生成服务器资产清单
Mar 01 Python
python使用正则表达式匹配字符串开头并打印示例
Jan 11 Python
python用pickle模块实现“增删改查”的简易功能
Jun 07 Python
linux环境下python中MySQLdb模块的安装方法
Jun 16 Python
python 利用文件锁单例执行脚本的方法
Feb 19 Python
PYQT5设置textEdit自动滚屏的方法
Jun 14 Python
python 内置函数汇总详解
Sep 16 Python
浅析Django 接收所有文件,前端展示文件(包括视频,文件,图片)ajax请求
Mar 09 Python
python图片指定区域替换img.paste函数的使用
Apr 09 Python
Python 如何对文件目录操作
Jul 10 Python
详解anaconda安装步骤
Nov 23 Python
python妙用之编码的转换详解
Apr 21 #Python
简单谈谈Python中的元祖(Tuple)和字典(Dict)
Apr 21 #Python
Python学习笔记之解析json的方法分析
Apr 21 #Python
Python正则抓取网易新闻的方法示例
Apr 21 #Python
Python中shape计算矩阵的方法示例
Apr 21 #Python
Python使用正则表达式抓取网页图片的方法示例
Apr 21 #Python
用python找出那些被“标记”的照片
Apr 20 #Python
You might like
PHP日期时间函数的高级应用技巧
2009/05/16 PHP
php 使用post,get的一种简洁方式
2010/04/25 PHP
php根据操作系统转换文件名大小写的方法
2014/02/24 PHP
PHP读取mssql json数据中文乱码的解决办法
2016/04/11 PHP
注意!PHP 7中不要做的10件事
2016/09/18 PHP
PHP使用redis位图bitMap 实现签到功能
2019/10/08 PHP
PHP语言对接抖音快手小红书视频/图片去水印API接口源码
2020/08/11 PHP
jQuery 1.0.4 - New Wave Javascript(js源文件)
2007/01/15 Javascript
两个select之间option的互相添加操作(jquery实现)
2009/11/12 Javascript
jQuery AJAX回调函数this指向问题
2010/02/08 Javascript
对JavaScript中this指针的新理解分享
2015/01/31 Javascript
JavaScript检测弹出窗口是否已经关闭的方法
2015/03/24 Javascript
jQuery左侧大图右侧小图焦点图幻灯切换代码分享
2015/08/19 Javascript
微信小程序 网络请求(post请求,get请求)
2017/01/17 Javascript
三种方式实现瀑布流布局
2017/02/10 Javascript
Angularjs 动态添加指令并绑定事件的方法
2017/04/13 Javascript
Javascript中click与blur事件的顺序详析
2017/04/25 Javascript
微信小程序调用摄像头隐藏式拍照功能
2018/08/22 Javascript
微信小程序视图容器(swiper)组件创建轮播图
2020/06/19 Javascript
vue中的面包屑导航组件实例代码
2019/07/01 Javascript
[54:19]完美世界DOTA2联赛PWL S2 Magma vs PXG 第二场 11.28
2020/12/01 DOTA
Python设计模式编程中Adapter适配器模式的使用实例
2016/03/02 Python
Python的几个高级语法概念浅析(lambda表达式闭包装饰器)
2016/05/28 Python
python使用PIL实现多张图片垂直合并
2019/01/15 Python
Python实现html转换为pdf报告(生成pdf报告)功能示例
2019/05/04 Python
python中Lambda表达式详解
2019/11/20 Python
Python turtle库绘制菱形的3种方式小结
2019/11/23 Python
Django模板之基本的 for 循环 和 List内容的显示方式
2020/03/31 Python
python模拟点击在ios中实现的实例讲解
2020/11/26 Python
python链表类中获取元素实例方法
2021/02/23 Python
请说出这段代码执行后a和b的值分别是多少
2015/03/28 面试题
JS原生实现轮播图的几种方法
2021/03/23 Javascript
工作中的自我评价如何写好
2013/10/28 职场文书
英文自荐信常用句子
2014/03/26 职场文书
求职自荐信的格式
2014/04/07 职场文书
中学生教师节演讲稿
2014/09/03 职场文书