Python with语句和__enter__、__exit__过程抽取思想
with语句的应用场景
编程中有很多操作都是配套使用的,这种配套的流程可以称为计算过程,Python语言为这种计算过程专门设计了一种结构:with语句。比如文件处理就是这类计算过程的典型代表。
使用with语句前后对比
没有使用with语句之前,我们是这样打开一个文件的:
try:
# 1. [进入]
f = open('a.txt', 'r', encoding="utf-8")
# 2. [执行]
print(f.read())
finally:
if f:
# 3. [退出]
f.close()
python操作文件的流程一般就是这三步:
- [进入]用只读方式打开文件
如果文件不存在,open()函数就会抛出一个IOError的错误,并且给出错误码和详细的信息告诉你文件不存在 - [执行]读取文件内容
如果文件打开成功,接下来,调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示 - [退出]关闭打开的文件
文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的
思考为什么关闭文件操作一定要放在finallly语句里?
由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用try ... finally来实现。
发现共性:
我们发现其实这种过程化的语句有共性,比如说在进去一个片段前必须做某种超赞,处理工作后又需要执行一个结束操作。比如上面的这段代码:
finally:
if f:
f.close()
上面的代码块就可以做一个封装。
使用with语句后,我们是这样打开一个文件的:
with open("a.txt", "r", encoding="utf-8") as f:
print(f.read())
这个with语句和前面的try ... finally结构是一样的,但是代码更佳简洁,并且不必调用f.close()方法。
with语句的执行原理
从解释器的角度去理解with语句执行流程。
with语句的基本形式是:
with 表达式 as 变量:
语句块
这样的一段代码可以称为一个上下文(context),在执行with语句时,解释器会先求出表达式的值,这个值(对象)是一个上下文管理器,并且假设这个对象拥有如下两个类魔术方法:
def __enter__():
# 描述进入上下文的动作
pass
def __exit__():
# 描述退出上下文的动作
pass
with语句在求出这个上下文管理器对象之后,自动执行进入方法,并将这个对象的返回值赋值于 as 之后的变量,然后执行语句块。然后在退出上下文前,自动执行对象的退出方法。
python系统和标准库的一些类型定义了这对操作,可以直接用于with语句。比如文件对象就直接支持这一对操作,因此可以用在with语句的头部。
如果你也有类似的计算过程需要抽取出来,那么可以自定义一个类,并且包含进入、退出方法。
自定义open函数
自己实现才发现,使用装饰器和生成器就能很好的解决这个问题,不需要用到类魔术方法来实现;
import contextlib # 引入上下文管理包
@contextlib.contextmanager # 给函数引入装饰器
def myopen(dir, mode):
print("开始")
f = open(dir, mode, encoding='utf-8')
try: # 上文
yield f
finally: # 下文
print("结束")
f.close()
with myopen("a.txt", 'r') as fobj: # 把try中的yield中的f赋值给fobj
# with会将with后面的函数中的yield赋值给fobj
for i in fobj:
print(i)
# 等待上面的循环结束后,才最终执行finally的代码,所以这就是上下文管理
输出:
开始
hello,我是a.txt的第1行文字。
结束
总结
打开文件读写、用pickle包完成数据的存储、恢复的操作,都非常适合使用with语句。
pickle包的使用案例:
try:
with open("phone.pickle", "wb") as outf:
pickle.dump("13193388105", outf)
except:
print("file have errow.")
try:
with open("phone.pickle", "rb") as outf:
data = pickle.load(outf)
print(type(data))
print(data)
except:
print("file have errow.")
我总结了一下使用with语句的优点:
采用with语句的代码更简洁;
防止因为忘记写f.close()而引发的错误;
一个对象(上下文)的操作有进入、退出过程就可以抽取出来,并做成自动化执行;
参考
《从问题到程序用Python编程和计算》
Python with语句和__enter__、__exit__过程抽取思想的更多相关文章
- with语句与__enter__,__exit__
class Foo(object): def func(self): print("func") pass def __enter__(self): print("ent ...
- python中的__enter__ __exit__
我们前面文章介绍了迭代器和可迭代对象,这次介绍python的上下文管理.在python中实现了__enter__和__exit__方法,即支持上下文管理器协议.上下文管理器就是支持上下文管理器协议的对 ...
- python - 上下文管理协议(with + __enter__ + __exit__)
上下文管理协议: with + __enter__ + __exit__ #上下问管理协议: #with + __enter__ + __exit__ class Test(): def __init ...
- Python with语句的概率,不多说了直接上代码!
python中的with语句用于访问资源.它确保执行指定的__exit__(“清理”)操作,而不管释放被访问资源的处理过程中的错误或异常,例如读取和写入文件后自动关闭.线程中锁的自动获取和释放等. p ...
- Python——with语句、context manager类型和contextlib库
目录 一.with语句 二.上下文管理器 三.contextlib模块 基本概念 上下文管理协议(Context Management Protocol) 包含方法 __enter__() 和 __e ...
- Python —条件语句
条件语句 Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块. 可以通过下图来简单了解条件语句的执行过程: Python程序语言指定任何非0和非空(null ...
- Python import语句导入模块语法[转]
Python import语句导入模块语法 社区推荐:掘金是国内最活跃的技术社区,我们每日有优质Python开发实例分享,海量python开源库推送.来掘金,和更多懂技术的小伙伴交流. pytho ...
- __enter__,__exit__上下文管理协议
上下文管理协议__enter__,__exit__ 用途或者说好处: 1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预 2.在需要管理一些资源比 ...
- Python pass 语句使用示例
Python pass 语句的使用方法示例.Python pass是空语句,pass语句什么也不做,一般作为占位符或者创建占位程序,是为了保持程序结构的完整性,pass语句不会执行任何操作,比如: P ...
随机推荐
- /bin/sh^M:解释器错误:没有那个文件或目录
在win下编辑的时候,换行结尾是\n\r , 而在linux下 是\n,所以才会有 多出来的\rsed -i 's/\r$//' configure 删除configure脚本中的\r
- Cassandra开发入门文档第五部分(使用场景)
正确建模 开发人员在构建Cassandra数据库时犯的另一个主要错误是分区键的选择不佳.cassandra是分布式的.这意味着您需要有一种方法来跨节点分布数据.Cassandra通过散列每个表的主键( ...
- IntelliJ IDEA 出现" java: 程序包javax.servlet不存在、 java: 程序包javax.servlet.annotation"等错误
在IDEA中建立Servlet使用javax.servlet.http.HttpServlet等类时,出现了如下错误: 原因:IntelliJ IDEA 没有导入 servlet-api.jar 这个 ...
- kubernetes基础知识点
1.Kubernetes Master主节点服务包括 Etcd┋kube-controller manager┋kube-apiserver┋kube-scheduler 2. Kubernetes ...
- Tomcat总结四种部署方式
静态部署 一.Webapps 把项目复制到Tomcat的Webapps 把war包复制到Tomcat的Webapps下 *修改Webapps的默认路径: * Tomcat的conf目录下的server ...
- Go基础编程实践(一)—— 操作字符串
修剪空格 strings包中的TrimSpace函数用于去掉字符串首尾的空格. package main import ( "fmt" "strings" ) ...
- mongoDB 分组并对分组结果筛选类似于SQL中的(group by xxx having ) 附带Java代码
今天需要做一个筛选程序,因为数据放在mongodb中,没写过分组的查询语句,查了一些资料,终于写出来了,分享给各位小伙伴 需求是 查询 学员 在2019-07-29之后未同步的数据(同一个学员需要2条 ...
- C# vb .net实现透视阴影特效滤镜
在.net中,如何简单快捷地实现Photoshop滤镜组中的透视阴影特效效果呢?答案是调用SharpImage!专业图像特效滤镜和合成类库.下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 ...
- C# 利用MS的 EntLib的Database类编写的DbHelper
C# 利用MS的 EntLib的Database类编写的DbHelper,由于MS的EntLib对Oracle.SQL Server和MySql已经封装,所以可以该DbHelper可以适用这三种数据库 ...
- 强大的Grafana k8s 插件
原文参考: https://i4t.com/4152.html 参考:https://blog.csdn.net/mailjoin/article/details/81389700 插件链接:http ...