文档章节

Python: try finally 与 上下文管理器简介

大数据之路
 大数据之路
发布于 2013/07/18 17:18
字数 1427
阅读 2066
收藏 4
点赞 1
评论 0

    用 Python 做一件很平常的事情: 打开文件, 逐行读入, 最后关掉文件; 进一步的需求是, 这也许是程序中一个可选的功能, 如果有任何问题, 比如文件无法打开, 或是读取出错, 那么在函数内需要捕获所有异常, 输出一行警告并退出. 代码可能一开始看起来是这样的

def read_file(): 
    try: 
        f = open('yui', 'r') 
        print ''.join(f.readlines()) 
    except: 
        print 'error occurs while reading file' 
    finally: 
        f.close()

    不过这显然无法运作, 因为  f  是在  try  块中定义的, 而在  finally  中无法引用.

    如果将  f  提取到  try  块外部, 如

def read_file(): 
     f = open('azusa', 'r') 
    try: 
        print ''.join(f.readlines()) 
    except: 
        print 'error occurs while reading file' 
    finally: 
        f.close()

那么, 问题在于当打开文件失败, 抛出异常将不会被捕获.

    挫一点的方法自然是, 再套一层  try  吧

def read_file(): 
     try: 
        f = open('sawako', 'r') 
        try: 
            print ''.join(f.readlines()) 
        except: 
            print 'error occurs while reading file' 
        finally: 
            f.close() 
     except: 
         print 'error occurs while reading file'

    当然这不仅仅是多一层缩进挫了, 连警告输出都白白多一次呢.

    正规一点的方式是, 使用 Python 引入的  with  结构来解决, 如

def readFile(): 
    try: 
         with open('mio', 'r')  as f: 
            print ''.join(f.readlines()) 
    except: 
        print 'error occurs while reading file'

    当文件打开失败时, 异常自然会被  except  到; 否则, 在  with  块结束之后, 打开的文件将自动关闭.

    除了打开文件, 还有其它这样可以用于  with  的东西么? 或者说, 怎么自定义一个什么东西, 让它能用于  with 呢?
    直接回答后一个问题吧, 秘密在于 Python 虚拟机在  with  块退出时会去寻找对象的  __exit__  方法并调用它, 把释放资源的动作放在这个  __exit__  函数中就可以了; 另外, 对象还需要一个  __enter__  函数, 当进入  with 块时, 这个函数被调用, 而它的返回值将作为  as  后引用的值. 一个简单的例子是

class Test: 
    def __init__(self): 
        print 'init' 

    def __enter__(self): 
        print 'enter' 
        return self 

    def __exit__(self, except_type, except_obj, tb): 
        print except_type 
        print except_obj 
        import traceback 
        print ''.join(traceback.format_tb(tb)) 
        print 'exit' 
        return True 

with Test() as t: 
    raise ValueError('kon!')

    执行这一段代码, 输出将会是

init 
enter 
<type 'exceptions.ValueError'> 
kon! 
  File "test.py", line 17, in <module> 
    raise ValueError('kon!') 

exit

     __exit__  函数接受三个参数, 分别是异常对象类型, 异常对象和调用栈. 如果  with  块正常退出, 那么这些参数将都是  None . 返回  True  表示发生的异常已被处理, 不再继续向外抛出.

    简单的介绍到此为止, 详细的情况可以参考  PEP 343  (这数字真不错, 7 3 ).

下面介绍下 with 语句的实例用法 & 高级用法:

Python高端、大气、上档次的with语句

在说with语句之前,先看看一段简单的代码吧

lock = threading.Lock()
...
lock.acquire()
elem = heapq.heappop(heap)
lock.release()

很简单直观,多个线程共用一个优先级队列的时候,首先先用互斥锁lock.acquire()把优先级队列锁上,然后取元素,再然后lock.release()释放这个锁。

虽然看似非常符合逻辑的一个过程,但是里面隐藏着一个巨大的bug:当heap里面没有元素的时候,会抛出一个IndexError异常,再然后堆栈回滚,再然后lock.release()根本不会执行,这个锁就永远得不到释放,因此就发生了喜闻乐见的死锁问题。这个也是很多大神们讨厌异常的原因。经典Java风格的解决方案就是

lock = threading.Lock()
...
lock.acquire()
try:
    elem = heapq.heappop(heap)
finally:
    lock.release()

这个虽然可以,但是怎么看怎么dirty,和Python优雅、简单的风格出入很大。其实,自从Python2.5开始引入了with语句,一切就变得非常简单:

lock = threading.Lock()
...
with lock:
    elem = heapq.heappop(heap)

在此无论以何种方式离开with语句的代码块,锁都会被释放。
with语句的设计目的就是为了使得之前需要通过try...finally解决的清理资源问题变得简单、清晰,它的的用法是

with expression [as variable]:
    with-block

其中expression返回一个叫做「context manager」的对象,然后这个对象被赋给variable(如果有的话)。「context manager」对象有两个方法,分别是__enter__()和__exit__(),很明显一个在进入with-block时调用,一个离开with-block的时候调用。

这样的对象不需要自己去实现,在Python标准库里面很多API都是已经实现了这两个方法,最常见的一个例子就是读写文件的open语句。

with open('1.txt', encoding = 'utf-8') as fp:
    lines = fp.readlines()

无论是正常离开还是因为异常原因离开with语句块,打开的文件资源总是会释放。
接下去讨论一下with语句配合contextlib库的一些比较实用的方法,比如需要同时打开两个文件,一个读一个写,这个时候就可以这样写:

from contextlib import nested
...
with nested(open('in.txt'), open('out.txt', 'w')) as (fp_in, fp_out):
    ...

这样就可以省掉两个with的语句的嵌套了,另外如果遇到一些还没有支持「context manager」的API呢?比如urllib.request.urlopen(),这个返回的对象因为不是「context manager」,结束的时候还需要自己去调用close方法。
类似这种API,contextlib提供了一个叫做closing方法,它会在离开with语句的时候,自动调用对象的close方法,因此urlopen也可以这样写:

from contextlib import closing
...
with closing(urllib.request.urlopen('http://www.yahoo.com')) as f:
    for line in f:
        sys.stdout.write(line)


参考资料:

[1] 8 PEP 343: The 'with' statement

https://docs.python.org/release/2.5/whatsnew/pep-343.html

Refer:http://ling0322.info/2013/10/03/python-with-statement.html

[2] Python 上下文管理器

http://python.jobbole.com/82289/

本文转载自:http://blog.bitfoc.us/?p=328

共有 人打赏支持
大数据之路
粉丝 1476
博文 501
码字总数 343984
作品 0
武汉
架构师
python中的上下文管理器

参考:http://blog.jobbole.com/64175/ 上下文管理器的任务是:代码块执行前准备,代码块执行后收拾 1、如何使用上下文管理器: 如何打开一个文件,并写入"hello world" filename="my.txt"mo...

指尖跳动的精灵 ⋅ 2015/04/08 ⋅ 0

王老板Python面试(10):17道python笔试面试真题

1、一行代码实现1--100之和 利用sum()函数求和 2、如何在一个函数内部修改全局变量 利用global 修改全局变量 3、列出5个python标准库 os:提供了不少与操作系统相关联的函数 sys: 通常用于命...

程序员八阿哥 ⋅ 05/22 ⋅ 0

Python 中的 with 语句与上下文管理器

对象,是 Python 对数据的抽象概念。Python 中的所有数据都是以对象或对象间的关系来实现的。(某种意义上,代码也是由对象来实现的,这与冯·诺依曼的“stored program computer”模型相一致...

lionets ⋅ 2014/01/26 ⋅ 0

Python学习笔记二十二(多继承 / 属性和方法 / 常量 / with和上下文管理器 )

多继承 狮虎兽, 不知道你有没有听说过? 狮虎兽,是雄狮(Panthera leo)与雌虎(Panthera tigris)杂交后的产物,是属于猫科豹属的一员. 用程序模拟一下狮虎兽. 当子类有多个父类 ( Lion 和 ...

DragonFangQy ⋅ 05/23 ⋅ 0

16道Python经典面试题及答案

1.Python是如何进行内存管理的? 答:从三个方面来说,一对象的引用计数机制,二垃圾回收机制,三内存池机制 一、对象的引用计数机制 Python内部使用引用计数,来保持追踪内存中的对象,所有对象...

阿豪boy ⋅ 05/17 ⋅ 0

云计算Python自动化:Python标识符讲解

开发人员在程序中自定义的一些符号和名称。标示符是自己定义的,如变量名,函数名等。在日常生活中,标示符是用来指定某个东西、人,要用到它,他或她的名字;在数学中解方程时,我们也常常用到...

长沙千锋 ⋅ 05/15 ⋅ 0

Python3.5从零开始学 - 第二章开始Py之旅

2.2 数据类型 数据类型分为6种: Number 数字 String 字符串 List 列表 Tuple 元祖 Sets 集合 Dictionary 字典 数值类型3种: int 整数 float 浮点数 complex 复数 2.2.1整型 整数与整数相除默...

九亿少女的电竞梦 ⋅ 05/31 ⋅ 0

Python开发简单爬虫--学习笔记

一、概述 目标 掌握开发轻量级爬虫 内容 爬虫简介 简单爬虫架构 URL管理器 网页下载器(urllib2) 网页解析器(BeautifulSoup) 完整实例:爬取百度百科雷军词条相关的1000个页面数据 tips: 轻量...

老男孩coder ⋅ 06/17 ⋅ 0

一份Python基础知识思维导图

如果说起最近几年发展势头比较猛的编程语言,可能想到最多的就是Python了。简单易用,模块丰富,功能强大,特别是机器学习的火爆,更是把Python推上了新的热度。相信点进来看的你也一定对Pyt...

JustMe23 ⋅ 05/31 ⋅ 0

新课上线|Python实现图片拼接与混合、XML-RPC文件共享程序等

关注「实验楼」,每天分享一个项目教程 最近又上线了哪些新课程?快来看看吧~ 会 员 课 一、《Python3 实现图片拼接与混合》 课程来源:selfim 课程简介:本课程主要是利用 Python 的第三方...

moy37rqw1jarn33bgzk ⋅ 05/12 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

005. 深入JVM学习—Java堆内存参数调整

1. JVM整体内存调整图解(调优关键) 实际上每一块子内存区域都会存在一部分可变伸缩区域,其基本流程:如果内存空间不足,则在可变的范围之内扩大内存空间,当一段时间之后,内存空间不紧张...

影狼 ⋅ 28分钟前 ⋅ 0

内存障碍: 软件黑客的硬件视图

此文为笔者近日有幸看到的一则关于计算机底层内存障碍的学术论文,并翻译(机译)而来[自认为翻译的还行],若读者想要英文原版的论文话,给我留言,我发给你。 内存障碍: 软件黑客的硬件视图...

Romane ⋅ 今天 ⋅ 0

SpringCloud 微服务 (七) 服务通信 Feign

壹 继续第(六)篇RestTemplate篇 做到现在,本机上已经有注册中心: eureka, 服务:client、order、product 继续在order中实现通信向product服务,使用Feign方式 下面记录学习和遇到的问题 贰 or...

___大侠 ⋅ 今天 ⋅ 0

gitee、github上issue标签方案

目录 [TOC] issue生命周期 st=>start: 开始e=>end: 结束op0=>operation: 新建issueop1=>operation: 评审issueop2=>operation: 任务负责人执行任务cond1=>condition: 是否通过?op3=>o......

lovewinner ⋅ 今天 ⋅ 0

浅谈mysql的索引设计原则以及常见索引的区别

索引定义:是一个单独的,存储在磁盘上的数据库结构,其包含着对数据表里所有记录的引用指针. 数据库索引的设计原则: 为了使索引的使用效率更高,在创建索引时,必须考虑在哪些字段上创建索...

屌丝男神 ⋅ 今天 ⋅ 0

String,StringBuilder,StringBuffer三者的区别

这三个类之间的区别主要是在两个方面,即运行速度和线程安全这两方面。 首先说运行速度,或者说是, 1.执行速度 在这方面运行速度快慢为:StringBuilder(线程不安全,可变) > StringBuffer...

时刻在奔跑 ⋅ 今天 ⋅ 0

java以太坊开发 - web3j使用钱包进行转账

首先载入钱包,然后利用账户凭证操作受控交易Transfer进行转账: Web3j web3 = Web3j.build(new HttpService()); // defaults to http://localhost:8545/Credentials credentials = Wallet......

以太坊教程 ⋅ 今天 ⋅ 0

Oracle全文检索配置与实践

Oracle全文检索配置与实践

微小宝 ⋅ 今天 ⋅ 0

mysql的分区和分表

1,什么是mysql分表,分区 什么是分表,从表面意思上看呢,就是把一张表分成N多个小表,具体请看mysql分表的3种方法 什么是分区,分区呢就是把一张表的数据分成N多个区块,这些区块可以在同一...

梦梦阁 ⋅ 今天 ⋅ 0

exception.ZuulException: Forwarding error

错误日志 com.netflix.zuul.exception.ZuulException: Forwarding error Caused by: com.netflix.hystrix.exception.HystrixRuntimeException: xxx timed-out and no fallback available. Ca......

jack_peng ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部