说到 with 大家通常看到的应该是这样的:

示例 1

with open('courses.txt') as f:for i in f:print(i.strip())

打开一个文件,然后循环做一些事情。但是你知道为什么会有 with 吗?我们自己是不是能够写出可以作用在 with 关键字上的对象呢?

现在,我们带着上述两个问题来说一说 with 的由来以及上下文管理器相关内容。

with 语句的目的是简化 try/finally 模式。这种模式用于保证一段代码运行完毕后执行某项操作,即便那段代码由于异常、return 语句或sys.exit() 调用而中止,也会执行指定的操作。finally 子句中的代码通常用于释放重要的资源,或者还原临时变更的状态。

示例1的功能我们可以使用 try/finally 模式实现:

示例2

try:f = open('courses.txt')for i in f:print(i.strip())
finally:f.close()

try中的 except 和 else 不是必须的,这里为了简单明了,我们只用了 finally。

对比两个示例,我们可以看到示例1相对简洁,这就是 with 的由来。

其实,语言中的一些特性或者说一些亮眼的特性,必然是有一个演化的过程的,我们作为后来者和使用者应该多花一些心思去思索其背后的实现过程,相信你会收获更多。

那么 上下文管理器 又是什么呢?

上下文管理器协议包含 __enter____exit__ 两个方法。with 语句开始运行时,会在上下文管理器对象上调用__enter__方法。with 语句运行结束后,会在上下文管理器对象上调用__exit__方法,以此扮演 finally 子句的角色。最常见的例子是确保关闭文件对象(示例1)。

下面我们实现一个简单的例子来直观的感受一下:

class T:def __enter__(self):print('T.__enter__')return '我是__enter__的返回值'def __exit__(self, exc_type, exc_val, exc_tb):print('T.__exit__')with T() as t:print(t)

输出:

T.__enter__
我是__enter__的返回值
T.__exit__

示例3中实现了一个类T,它的对象包含了__enter____exit__方法,有了这两个方法就可以使用 with 处理该对象。执行 with 后面的表达式T()得到的是上下文管理器对象,通过as字句把对象绑定到了变量t上。

观察输出结果,可以看到with块先调用了__enter__方法,在处理完内部逻辑(print(t))之后调用了exit方法,而t其实就是__enter__方法的返回值。

当然,这个例子只是为了方便我们理解上下文管理器,下面我们看一个更有意思的例子:

示例4

obj1 = HaHa('你手机拿反了')
with obj1 as content:print('哈哈镜花缘')print(content)
print('#### with 执行完毕后,再输出content: ####')
print(content)

输出:

缘花镜哈哈
了反拿机手你
#### with 执行完毕后,在输出content: ####
你手机拿反了

示例4中,上下文管理器是 HaHa 类的实例,Python 调用此实例的__enter__方法,把返回结果绑定到 变量content 上。

打印一个字符串,然后打印 content 变量的值。可以看到打印出的内容都是是反向的。

最后,当 with 块已经执行完毕。可以看出,__enter__ 方法返回的值——即存储在 content 变量中的值——是字符串 ‘你手机拿反了’。
输出不再是反向的了。

HaHa类的实现:

import sysclass HaHa:def __init__(self, word):self.word = worddef reverse_write(self, text):self.original_write(text[::-1])def __enter__(self):self.original_write = sys.stdout.writesys.stdout.write = self.reverse_writereturn self.worddef __exit__(self, exc_type, exc_value, traceback):sys.stdout.write = self.original_writereturn True

在__enter__方法中,我们接管了标准输出,将其替换成我们自己编写的方法reverse_write,reverse_write方法将参数内容反转。而在__exit__方法中,我们将标准输出还原。__exit__方法需要返回True。

总之,with之于上下文管理器,就像for之于迭代器一样。with就是为了方便上下文管理器的使用。

上下文管理器特性在标准库中有一些应用:

在 sqlite3 模块中用于管理事务;在 threading 模块中用于维护锁、条件和信号;

另外,说到上下文管理器就不得不提一下@contextmanager 装饰器,它能减少创建上下文管理器的样板代码量,因为不用编写一个完整的类,定义 __enter____exit__ 方法,而只需实现有一个 yield 语句的生成器,生成想让 __enter__ 方法返回的值。

在使用 @contextmanager 装饰的生成器中,yield 语句的作用是把函数的定义体分成两部分:

  • yield 语句前面的所有代码在 with 块开始时(即解释器调用 __enter__ 方法时)执行

  • yield 语句后面的代码在with 块结束时(即调用 __exit__ 方法时)执行。

下面我们用 @contextmanager 装饰器来实现一下示例4的功能:

示例5

'''
学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import sys
import contextlib@contextlib.contextmanager
def WoHa(n):original_write = sys.stdout.writedef reverse_write(text):original_write(text[::-1])sys.stdout.write = reverse_writeyield nsys.stdout.write =  original_writereturn Trueobj1 = WoHa('你手机拿反了')
with obj1 as content:print('哈哈镜花缘')print(content)
print('#### with 执行完毕后,在输出content: ####')
print(content)

输出:

缘花镜哈哈
了反拿机手你
#### with 执行完毕后,在输出content: ####
你手机拿反了

这里我们需要注意的是:代码执行到yield时,会产出一个值,这个值会绑定到 with 语句中 as 子句的变量上。执行 with 块中的代码时,这个函数会在yield这里暂停。此时,相当于示例4中执行完__enter__方法。而控制权一旦跳出 with 块(块内代码执行完毕)则继续执行 yield 语句之后的代码。

@contextmanager 装饰器优雅且实用,把三个不同的 Python 特性结合到了一起:函数装饰器、生成器和 with 语句。

现在,我想你应该能够解答开篇提到的两个问题了吧!

Python教程:with ... as 语句你懂嘛?相关推荐

  1. Python if else条件语句你懂了吗?

    在 Python 中,可以使用 if else 语句对条件进行判断,然后根据不同的结果执行不同的代码,这称为选择结构或者分支结构. Python 中的 if else 语句可以细分为三种形式,分别是 ...

  2. 420集的python教程视频_阿里达摩院推的420集的python教程高清版,据说懂中文就能入门...

    阿里达摩院推的400集的python教程高清版,据说懂中文就能入门 小编的内心是强大的,网友虐我千百遍,我待网友如初恋,因为今天又给大家带来了干货,Python入门教程完整版,完整版啊!完整版! 为了 ...

  3. python基础一入门必备知识-python基础教程#菜鸟也能看懂的超简单入门必备知识...

    python基础语法教程 python一直是一门非常火爆的编程语言,从简洁的语法,全面的功能,也是得到许多编程萌新的重视,在刚刚接触编程不久的人来说,python就像是代码编程的入门语言,但其实想成为 ...

  4. python爬虫简单实例-最简单的Python爬虫案例,看得懂说明你已入门,附赠教程

    原标题:最简单的Python爬虫案例,看得懂说明你已入门,附赠教程 这是最简单的Python爬虫案例,如果你能看懂,那么请你保持信心,因为你已经入门Python爬虫,只要带着信心和努力,你的技术能力在 ...

  5. python教程循环语句,Python基础教程之循环语句(for、while和嵌套循环)

    循环可以用来重复执行某条语句,直到某个条件得到满足或遍历所有元素. 1 for循环 是for循环,可以把集合数据类型list.tuple.dict.set的元素遍历出来. (1)对list进行循环 c ...

  6. Python教程:Python中的for 语句

    Python 中的 for 语句与你在 C 或 Pascal 中可能用到的有所不同. Python教程 中的 for 语句并不总是对算术递增的数值进行迭代(如同 Pascal),或是给予用户定义迭代步 ...

  7. python编程入门必备知识-python基础教程#菜鸟也能看懂的超简单入门必备知识

    python基础语法教程 python一直是一门非常火爆的编程语言,从简洁的语法,全面的功能,也是得到许多编程萌新的重视,在刚刚接触编程不久的人来说,python就像是代码编程的入门语言,但其实想成为 ...

  8. python教程07-while语句的基本使用、for...in循环的使用、break与continue、打印矩形三角形九九乘法表、基础题、进阶题

    python教程_小白入门/2020/7/20 行百里者半九十,你可一定要坚持下去啊 前几天家里有事更新给耽误了,后续会稳定更新的,一起加油! 学习目标 文章目录 python教程_小白入门/2020 ...

  9. 零基础Python完全自学教程11:Python中的选择语句

    欢迎你来到站长学堂,学习站长在线出品的在线课程<零基础Python完全自学教程>今天给大家分享的是第11课<Python中的选择语句>.本节课主要内容有:最简单的if语句.if ...

  10. python编码转换语句_好程序员Python教程之字符串编码知识小结

    好程序员Python教程之字符串编码知识小结,提及Python字符串,你会想到什么?是ASCII,还是Unicode?他们之间是如何转换的?字符串编码和字符串有什么区别?接下来好程序员Python教程 ...

最新文章

  1. PCL—低层次视觉—点云分割(基于凹凸性)
  2. 《雷达技术丛书》分享
  3. npm的插件如何直接在html中使用,webpack插件之htmlWebpackPlugin
  4. 8 .5 .5 创建操作员
  5. 音视频技术开发周刊 | 149
  6. SANS研究所:7大最危险的攻击技术介绍
  7. [032] 微信公众帐号开发教程第8篇-文本消息中使用网页超链接(转)
  8. (23)FPGA面试题常用逻辑电平
  9. MYSQL数据库中触发器禁用、启用、查询
  10. html调试的时候弹出一直消失,页面中css调试和问题解决的一些经验总结
  11. 配置多个git账号或多个SSH账号
  12. centos7安装源疯了_Jenkins 在 Centos7 上安装(使用国内源)
  13. 如何注册和删除系统服务文件
  14. sql常用函数详解(一)——字符串截取
  15. JAVA基础语言——JAVASE
  16. Excel表格样式CellStyle的DataFormat可选值
  17. 【控制control】动力学基础
  18. 项目中报Assign array to a variable before exporting as module default 这个错
  19. Lwip之PPP、PPPoE实现(一)
  20. 强监管焕新外卖行业,美团、饿了么如何应对?

热门文章

  1. Rhel7/Centos7 修改运行级别
  2. H3C 路由器配置console密码登录[学习]
  3. python 抓取网页(一)
  4. oracle中导入导出数据备份数据库
  5. Inotify+Rsync实现linux文件实时同步
  6. java正则表达式对象_Java正则表达式之Pattern类
  7. 倍福模块通讯协议_认识倍福(Beckhoff)CX5100系列嵌入式控制器
  8. usestate中的回调函数_React Hooks 源码解析(3):useState
  9. 【MM模块】 Goods Receipt 收货 3
  10. 21、Power Query-列文本格式处理