上下文管理器(context manager)是 Python 编程中的重要概念,用于规定某个对象的使用范围。一旦进入或者离开该使用范围,会有特殊操作被调用 (比如为对象分配或者释放内存)。它的语法形式是with...as...

为了确保一些系统资源得以正确释放,我们经常会用到 try ... excepte ... finally 语句。如:

try:
f = open('somefile')
for line in f:
print(line)
except Exception as e:
print(e)
finally:
f.close()

上面的代码模式,从用复用代码的模式来讲,并不够好。于是 with 语句出现了,通过定义一个上下文管理器来封装这个代码块:

with open('filename', 'r') as f:
for line in f:
print(line)

显然,with 语句比 try 语句简洁了许多。

上下文管理器类

由于 with 语句利用了上下文管理器,在深入理解 with 语句之前,我们先看看上下文管理器。我们要定义一个上下文管理器其实很简单,只要一个类实现了__enter__(self)和__exit__(self, exc_type, exc_valye, traceback)我们就叫他上下文管理器

__enter__(self) 返回一个对象,可以是当前类的实例,也可以是其他对象。

class SomeThing:
def __enter__(self):
return self # 返回类实例 class LineLength:
def __init__(self, filepath):
self.__file = open(self.__filepath) def __enter__(self):
return self.__file # 返回其他对象

执行过程

下面让我们看看 with 语句具体是如何执行的。

第一步:执行上下文表达式以获得上下文管理器对象。上下文表达式就是 with 和 as 之间的代码。

第二步:加载上下文管理器对象的 __exit__()方法备用。

第三步:执行上下文管理器对象的__enter__()方法。

第四步:将__enter__()方法返回值绑定到 as 后面的 变量中。

第五步:执行 with 内的代码块。

第六步:执行上下文管理器的__exit__()方法。

如果在代码块中发生了异常,异常被传入__exit__()中。如果没有,__exit__()的三个参数会传入 None, None, None。__exit__()需要明确地返回 True 或 False。并且不能在__exit__()中再次抛出被传入的异常,这是解释器的工作,解释器会根据返回值来确定是否继续向上层代码传递异常。当返回 True 时,异常不会被向上抛出,代码会从报异常的代码处跳出上下文管理继续执行代码,当返回 False 时会向上抛出,阻断代码执行。当没有异常发生传入__exit__()时,解释器会忽略返回值。

import time

class File(object):
def __init__(self, filename, mode):
print("上下文管理执行顺序:\n1、执行初始化方法")
self.f = open(filename, mode) def __enter__(self):
print("2、执行__enter__()方法")
return self # "__enter__()方法的返回值绑定到 as 后面的 变量中 def __exit__(self, exc_type, exc_val, exc_tb):
"""
:param exc_type:如果抛出异常,返回异常类型,否则返回None
:param exc_val:如果抛出异常,返回异常内容,否则返回None
:param exc_tb:如果抛出异常,返回异常位置,否则返回None
:return:
"""
print("4、执行__exit__()方法")
if exc_type:
print(exc_type, "\n", exc_val, "\n", exc_tb) # 若代码块报错则立即执行__exit__()方法中的代码;若代码块没报错,执行完代码块后执行__exit__()方法中的代码"
self.f.close()
time.sleep(1)
# return True # 当返回 True 时,异常不会被向上抛出,代码会从报异常的代码处跳出上下文管理继续执行代码,
return False # 当返回 False 时会在__exit__()方法执行完后向上抛出,阻断代码执行。 def reads(self):
return self.f.read() with File("README.md", "rb") as f:
print("3、执行代码块代码")
# 代码块部分
f.reads()
# 代码块报错,报错后的代码不会被执行
print("若代码块报错,“我”不被执行;将r改为rb,代码块不报错“我”就执行")
print("若代码块报错,__exit__()方法中返回 Ture “我”才执行;若代码块不报错,“我”也执行")

执行结果:

把"rb"改为"r"后执行结果:

多上下文管理器

实际上,我们可以同时处理多个上下文管理器:

with A() as a, B() as b:
suite

所以我们大可不必写嵌套的 with 语句。

contextmanager实现上下文管理器

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

from contextlib import contextmanager

class File:
def __init__(self, filename, mode):
self.f = open(filename, mode) def reads(self):
return self.f.read() def closed(self):
self.f.close() @contextmanager
def read_file():
print(1)
rf = File("README.md", "rb")
yield rf
print(3)
rf.closed() with read_file() as f:
print(2)
f.reads()

执行结果:

把"rb"改为"r"后执行结果:

注:当报错后,没有打印3,不清楚yield下面的代码是否执行。

Python_上下文管理器的更多相关文章

  1. python2.7高级编程 笔记一(Python中的with语句与上下文管理器学习总结)

    0.关于上下文管理器上下文管理器是可以在with语句中使用,拥有__enter__和__exit__方法的对象. with manager as var: do_something(var) 相当于以 ...

  2. 翻译《Writing Idiomatic Python》(五):类、上下文管理器、生成器

    原书参考:http://www.jeffknupp.com/blog/2012/10/04/writing-idiomatic-python/ 上一篇:翻译<Writing Idiomatic ...

  3. python学习笔记4(对象/引用;多范式; 上下文管理器)

    ### Python的强大很大一部分原因在于,它提供有很多已经写好的,可以现成用的对象 21. 动态类型:对象/引用 对象和引用: 对象是储存在内存中的实体,对象名只是指向这一对象的引用(refere ...

  4. Python深入02 上下文管理器

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 上下文管理器(context manager)是Python2.5开始支持的一种语 ...

  5. python 上下文管理器

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 上下文管理器(context manager)是Python2.5开始支持的一种语 ...

  6. 【Python学习笔记】with语句与上下文管理器

    with语句 上下文管理器 contextlib模块 参考引用 with语句 with语句时在Python2.6中出现的新语句.在Python2.6以前,要正确的处理涉及到异常的资源管理时,需要使用t ...

  7. python上下文管理器及with语句

    with语句支持在一个叫上下文管理器的对象的控制下执行一系列语句,语法大概如下: with context as var: statements 其中的context必须是个上下文管理器,它实现了两个 ...

  8. 流畅python学习笔记:第十五章:上下文管理器

    在开始本章之前,我们首先来谈谈try-excep..final模块.在Python中,进行异常保护的最多就是用try..except..final.首先来看下下面的代码.进行一个简单的除法运算.为了防 ...

  9. python contextlib 上下文管理器

    1.with操作符 在python中读写文件,可能需要这样的代码 try-finally读写文件 file_text = None try: file_text = open('./text', 'r ...

随机推荐

  1. Hibernate的基本功能:对数据库的增删改查(创建对象实例)

    一.通过实例化的对象向数据库添加新记录 package com.yh.test; import org.hibernate.Session; import org.hibernate.SessionF ...

  2. java的父类声明,子类实例化(强制类型转换导致异常ClassCastException)

    一.使用原因 父类声明,子类实例化,既可以使用子类强大的功能,又可以抽取父类的共性. 二.使用要点 1.父类类型的引用可以调用父类中定义的所有属性和方法: 2.父类中方法只有在是父类中定义而在子类中没 ...

  3. 【力扣】95. 不同的二叉搜索树 II

    二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的 ...

  4. 我的第一篇博客blog,笑哭

    我的第一篇博客blog Markdown学习 一级标题:#加一个空格 加 文字, 二级标题:加2个##以此类推 字体 粗体:hello world!字体前有二个星号,字体后有二个星号 斜体:hello ...

  5. 培训班输出的大量学员,会对IT行业产生哪些影响?

    先说下会有哪些影响呢?   1 可能也就是些大城市的,规模比较大的,口碑比较好的培训学校输出的码农才能入行,而且能做长久.一些线上的所谓培训机构,或者小城市的培训学校,输出的能入行的码农,其实规模很有 ...

  6. Python绘制饼图

    Python绘制饼图 1.1 对应代码如下图所示 import matplotlib.pyplot as pltfrom pylab import mplmpl.rcParams['font.sans ...

  7. Linux centos 安装Docker

    安装docker需要centos7 内核3.1以上 基本上centos7以上的都支持, 然后先更新到最新 sudo yum update 然后直接安装 sudo yum install docker ...

  8. Sql Server 索引笔记

    CREATE UNIQUE  CLOSTERED INDEX Idx_phone  ON  teacher (t_phone  DESC)  WITH  FILLFACTOR=30; 如果表中定义了主 ...

  9. 联盛德 HLK-W806 (十二): Makefile组织结构和编译流程说明

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  10. JAVA使用百度链接实时推送API提交链接

    官网地址:http://data.zz.baidu.com/ 百度推广API的token获取 http://data.zz.baidu.com/site/index 填写完之后会进行验证, 验证完之后 ...