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操作文件的流程一般就是这三步:

  1. [进入]用只读方式打开文件

    如果文件不存在,open()函数就会抛出一个IOError的错误,并且给出错误码和详细的信息告诉你文件不存在
  2. [执行]读取文件内容

    如果文件打开成功,接下来,调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示
  3. [退出]关闭打开的文件

    文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的

思考为什么关闭文件操作一定要放在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语句的优点:

  1. 采用with语句的代码更简洁;

  2. 防止因为忘记写f.close()而引发的错误;

  3. 一个对象(上下文)的操作有进入、退出过程就可以抽取出来,并做成自动化执行;

参考

《从问题到程序用Python编程和计算》

最新文章

  1. MyBatis在insert插入操作时返回主键ID的配置
  2. CentOS7 安装Nginx
  3. 谢欣伦 - OpenDev原创例程 - 时间同步Time Sync
  4. ATC空管系统的实时控制软件系统分析
  5. yum安装 lnmp
  6. mvc路由设置参数配置类似于url重写
  7. CentOS6.5安装MySQL及完全卸载
  8. 常见HTTP状态码大全
  9. 『重构--改善既有代码的设计』读书笔记----Move Field
  10. LESS使用介绍
  11. Spring——Web应用中的IoC容器创建(WebApplicationContext根应用上下文的创建过程)
  12. linux管理面板
  13. 使用钩子参与到TCP拥塞事件的处理中
  14. mongodb 3.6 集群搭建:分片+副本集
  15. HTTP Server to Client Communication
  16. 在listView中的模糊查询和删除
  17. vs2015打开慢的解决方法
  18. 【学习】Hall’s Marriage Theorem
  19. jupyter notebook远程服务器终端连接
  20. VS2015编译提示无法运行“rc.exe”

热门文章

  1. 转 zabbix 自动发现和 zabbix自定义用户key与参数User parameters
  2. BladeX 部署centos启动顺序
  3. Tensorflow不同版本要求与CUDA及CUDNN版本对应关系
  4. Oracle 实现表中id字段自增长
  5. Ubuntu18.04LTS python3.6 cuda10.0 下安装低版本的pytorch
  6. SQL Server 系统库的备份与恢复
  7. [转帖]插曲:大白话带你认识Kafka
  8. IUrlHelper ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
  9. AVR单片机教程——EasyElectronics Library v1.3手册
  10. Go基础编程实践(七)—— 并发