上下文管理器(Context managers)

上下文管理器允许你在有需要的时候,精确地分配和释放资源。

使用上下文管理器最广泛的案例就是with语句了。
想象下你有两个需要结对执行的相关操作,然后还要在它们中间放置一段代码。
上下文管理器就是专门让你做这种事情的。举个例子:

with open('some_file', 'w') as opened_file:
opened_file.write('Hola!')

上面这段代码打开了一个文件,往里面写入了一些数据,然后关闭该文件。如果在往文件写数据时发生异常,它也会尝试去关闭文件。上面那段代码与这一段是等价的:

file = open('some_file', 'w')
try:
file.write('Hola!')
finally:
file.close()

当与第一个例子对比时,我们可以看到,通过使用with,许多样板代码(boilerplate code)被消掉了。 这就是with语句的主要优势,它确保我们的文件会被关闭,而不用关注嵌套代码如何退出。

上下文管理器的一个常见用例,是资源的加锁和解锁,以及关闭已打开的文件(就像我已经展示给你看的)。

让我们看看如何来实现我们自己的上下文管理器。这会让我们更完全地理解在这些场景背后都发生着什么。

基于类的实现

一个上下文管理器的类,最起码要定义__enter____exit__方法。
让我们来构造我们自己的开启文件的上下文管理器,并学习下基础知识。

class File(object):
def __init__(self, file_name, method):
self.file_obj = open(file_name, method)
def __enter__(self):
return self.file_obj
def __exit__(self, type, value, traceback):
self.file_obj.close()

通过定义__enter____exit__方法,我们可以在with语句里使用它。我们来试试:

with File('demo.txt', 'w') as opened_file:
opened_file.write('Hola!')

我们的__exit__函数接受三个参数。这些参数对于每个上下文管理器类中的__exit__方法都是必须的。我们来谈谈在底层都发生了什么。

  1. with语句先暂存了File类的__exit__方法
  2. 然后它调用File类的__enter__方法
  3. __enter__方法打开文件并返回给with语句
  4. 打开的文件句柄被传递给opened_file参数
  5. 我们使用.write()来写文件
  6. with语句调用之前暂存的__exit__方法
  7. __exit__方法关闭了文件

处理异常

我们还没有谈到__exit__方法的这三个参数:typevaluetraceback
在第4步和第6步之间,如果发生异常,Python会将异常的type,valuetraceback传递给__exit__方法。
它让__exit__方法来决定如何关闭文件以及是否需要其他步骤。在我们的案例中,我们并没有注意它们。

那如果我们的文件对象抛出一个异常呢?万一我们尝试访问文件对象的一个不支持的方法。举个例子:

with File('demo.txt', 'w') as opened_file:
opened_file.undefined_function('Hola!')

我们来列一下,当异常发生时,with语句会采取哪些步骤。 1. 它把异常的type,valuetraceback传递给__exit__方法 2. 它让__exit__方法来处理异常 3. 如果__exit__返回的是True,那么这个异常就被优雅地处理了。 4. 如果__exit__返回的是True以外的任何东西,那么这个异常将被with语句抛出。

在我们的案例中,__exit__方法返回的是None(如果没有return语句那么方法会返回None)。因此,with语句抛出了那个异常。

Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'file' object has no attribute 'undefined_function'

我们尝试下在__exit__方法中处理异常:

class File(object):
def __init__(self, file_name, method):
self.file_obj = open(file_name, method)
def __enter__(self):
return self.file_obj
def __exit__(self, type, value, traceback):
print("Exception has been handled")
self.file_obj.close()
return True with File('demo.txt', 'w') as opened_file:
opened_file.undefined_function() # Output: Exception has been handled

我们的__exit__方法返回了True,因此没有异常会被with语句抛出。

基于生成器的实现

我们还可以用装饰器(decorators)和生成器(generators)来实现上下文管理器。
Python有个contextlib模块专门用于这个目的。我们可以使用一个生成器函数来实现一个上下文管理器,而不是使用一个类。
让我们看看一个基本的,没用的例子:

from contextlib import contextmanager

@contextmanager
def open_file(name):
f = open(name, 'w')
yield f
f.close()

OK啦!这个实现方式看起来更加直观和简单。然而,这个方法需要关于生成器、yield和装饰器的一些知识。在这个例子中我们还没有捕捉可能产生的任何异常。它的工作方式和之前的方法大致相同。

让我们小小地剖析下这个方法。

  1. Python解释器遇到了yield关键字。因为这个缘故它创建了一个生成器而不是一个普通的函数。

  2. 因为这个装饰器,contextmanager会被调用并传入函数名(open_file)作为参数。

  3. contextmanager函数返回一个以GeneratorContextManager对象封装过的生成器。

  4. 这个GeneratorContextManager被赋值给open_file函数,我们实际上是在调用GeneratorContextManager对象。

那现在我们既然知道了所有这些,我们可以用这个新生成的上下文管理器了,像这样:

with open_file('some_file') as f:
f.write('hola!')

Python上下文管理器(Context managers)的更多相关文章

  1. Python上下文管理器(context manager)

    上下文管理器(context manager)是Python2.5开始支持的一种语法,用于规定某个对象的使用范围.一旦进入或者离开该使用范围,会有特殊操作被调用 (比如为对象分配或者释放内存).它的语 ...

  2. Python 上下文管理器模块--contextlib

    在 Python 处理文件的时候我们使用 with 关键词来进行文件的资源关闭,但是并不是只有文件操作才能使用 with 语句.今天就让我们一起学习 Python 中的上下文管理 contextlib ...

  3. python 上下文管理器

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

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

    http://blog.csdn.net/pipisorry/article/details/50444736 with语句 with语句是从 Python 2.5 开始引入的一种与异常处理相关的功能 ...

  5. python 上下文管理器contextlib.ContextManager

    1 模块简介 在数年前,Python 2.5 加入了一个非常特殊的关键字,就是with.with语句允许开发者创建上下文管理器.什么是上下文管理器?上下文管理器就是允许你可以自动地开始和结束一些事情. ...

  6. 吃透Python上下文管理器

    什么是上下文管理器? 我们常见的with open操作文件,就是一个上下文管理器.如: with open(file, 'rb') as f: text = f.read() 那上下文管理器具体的定义 ...

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

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

  8. Python 上下文管理器和else块

    最终,上下文管理器可能几乎与子程序(subroutine)本身一样重要.目前,我们只了解了上下文管理器的皮毛--Basic 语言有with 语句,而且很多语言都有.但是,在各种语言中 with 语句的 ...

  9. Python上下文管理器

    在Python中让自己创建的函数.类.对象支持with语句,就实现了上线文管理协议.我们经常使用with open(file, "a+") as f:这样的语句,无需手动调用f.c ...

随机推荐

  1. INFO org.apache.hadoop.ipc.RPC: Server at master/192.168.200.128:9000 not available yet, Zzzzz...

    hadoop 启动时namenode和datanode可以启动,使用jps命令也可以看到进程,但是在浏览器中输入master:50070却没有显示datanode 查看datanode的log日志: ...

  2. Spring Boot使用Feign客户端调用远程服务时出现:timed-out and no fallback available,failed and no fallback available的问题解决

    timed-out and no fallback available: 这个错误基本是出现在Hystrix熔断器,熔断器的作用是判断该服务能不能通,如果通了就不管了,调用在指定时间内超时时,就会通过 ...

  3. kis

    http://5.xp510.com:801/xp2011/%E9%87%91%E8%9D%B6kis%E4%B8%93%E4%B8%9A%E7%89%88.rar

  4. 我被C++开发欺辱的岁月

    前言 人被压迫了,为什么不斗争?——鲁迅 作为一个C#开发者,我经历了,也见证了很多同行饱受C++开发的歧视和欺辱. 而且,这种行为,现在依然持续的发生在C#开发者的身上,就目前为止,绝大部分C#开发 ...

  5. 系统重装 Ghost系统的disk to image等等是什么意思

    localdiskto disk to imade from imagepartitionto partition to image from imagecheckimage file disk这些是 ...

  6. #define中的#和##作用

    #define语句中的#是把参数字符串化,##是连接两个参数成为一个整体. #define FACTORY_REF(name) { #name, Make##name } 中#name就是将传入的na ...

  7. 使用literal语法格式化字符串

    支持arm64之后,格式化字符串的时候会遇到一些问题,主要与NSInteger的定义有关: #if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET ...

  8. CodeForces - 344D Alternating Current (模拟题)

    id=46667" style="color:blue; text-decoration:none">CodeForces - 344D id=46667" ...

  9. 启动两个Tomcat的方法

     由于项目需要,所以要启动两个工程,但是又不能用一个Tomcat,于是就琢磨起了怎么启动两个Tomcat 1:首先,conf/server.xml要把HTTP的端口改成不一致的,我一个是8088,一个 ...

  10. php截取某二个特殊字符串间的某段字符串

    在php开发的过程中,有时候会用到截取某二个特殊字符串间的某个字符串,并对这个字符串做特殊的处理,那么对截取出来的字符串做什么特殊处理我们临时无论.我们今天先讲php截取某二个特殊字符串间的某个字符串 ...