python中为什么推荐使用with_Python中的with关键字使用详解

这篇文章主要介绍了Python 中的with关键字使用详解的相关资料,在Python中,with关键字是一个替你管理实现上下文协议对象的好东西,需要的朋友可以参考下

">

在 Python 2.5 中, with 关键字被加入。它将常用的 try ... except ... finally ... 模式很方便的被复用。看一个最经典的例子:with open('file.txt') as f:

content = f.read()

在这段代码中,无论 with 中的代码块在执行的过程中发生任何情况,文件最终都会被关闭。如果代码块在执行的过程中发生了一个异常,那么在这个异常被抛出前,程序会先将被打开的文件关闭。

再看另外一个例子。

在发起一个数据库事务请求的时候,经常会用类似这样的代码:db.begin()

try:

# do some actions

except:

db.rollback()

raise

finally:

db.commit()

如果将发起事务请求的操作变成可以支持 with 关键字的,那么用像这样的代码就可以了:with transaction(db):

# do some actions

下面,详细的说明一下 with 的执行过程,并用两种常用的方式实现上面的代码。

with 的一般执行过程

一段基本的 with 表达式,其结构是这样的:with EXPR as VAR:

BLOCK

其中: EXPR 可以是任意表达式; as VAR 是可选的。其一般的执行过程是这样的:计算 EXPR ,并获取一个上下文管理器。

上下文管理器的 exit() 方法被保存起来用于之后的调用。

调用上下文管理器的 enter() 方法。

如果 with 表达式包含 as VAR ,那么 EXPR 的返回值被赋值给 VAR 。

执行 BLOCK 中的表达式。

调用上下文管理器的 exit() 方法。如果 BLOCK 的执行过程中发生了一个异常导致程序退出,那么异常的 type 、 value 和 traceback (即 sys.exc_info()的返回值 )将作为参数传递给 exit() 方法。否则,将传递三个 None 。

将这个过程用代码表示,是这样的:mgr = (EXPR)

exit = type(mgr).exit # 这里没有执行

value = type(mgr).enter(mgr)

exc = True

try:

try:

VAR = value # 如果有 as VAR

BLOCK

except:

exc = False

if not exit(mgr, *sys.exc_info()):

raise

finally:

if exc:

exit(mgr, None, None, None)

这个过程有几个细节:

如果上下文管理器中没有 enter() 或者 exit() 中的任意一个方法,那么解释器会抛出一个 AttributeError 。

在 BLOCK 中发生异常后,如果 exit() 方法返回一个可被看成是 True 的值,那么这个异常就不会被抛出,后面的代码会继续执行。

接下来,用两种方法来实现上面来实现上面的过程的吧。

实现上下文管理器类

第一种方法是实现一个类,其含有一个实例属性 db 和上下文管理器所需要的方法 enter() 和 exit() 。class transaction(object):

def init(self, db):

self.db = db

def enter(self):

self.db.begin()

def exit(self, type, value, traceback):

if type is None:

db.commit()

else:

db.rollback()

了解 with 的执行过程后,这个实现方式是很容易理解的。下面介绍的实现方式,其原理理解起来要复杂很多。

使用生成器装饰器

在Python的标准库中,有一个装饰器可以通过生成器获取上下文管理器。使用生成器装饰器的实现过程如下:from contextlib import contextmanager

@contextmanager

def transaction(db):

db.begin()

try:

yield db

except:

db.rollback()

raise

else:

db.commit()

第一眼上看去,这种实现方式更为简单,但是其机制更为复杂。看一下其执行过程吧:Python解释器识别到 yield 关键字后, def 会创建一个生成器函数替代常规的函数(在类定义之外我喜欢用函数代替方法)。

装饰器 contextmanager 被调用并返回一个帮助方法,这个帮助函数在被调用后会生成一个 GeneratorContextManager 实例。最终 with 表达式中的 EXPR 调用的是由 contentmanager 装饰器返回的帮助函数。

with 表达式调用 transaction(db) ,实际上是调用帮助函数。帮助函数调用生成器函数,生成器函数创建一个生成器。

帮助函数将这个生成器传递给 GeneratorContextManager ,并创建一个 GeneratorContextManager 的实例对象作为上下文管理器。

with 表达式调用实例对象的上下文管理器的 enter() 方法。

enter() 方法中会调用这个生成器的 next() 方法。这时候,生成器方法会执行到 yield db 处停止,并将 db 作为 next() 的返回值。如果有 as VAR ,那么它将会被赋值给 VAR 。

with 中的 BLOCK 被执行。

BLOCK 执行结束后,调用上下文管理器的 exit() 方法。 exit() 方法会再次调用生成器的 next() 方法。如果发生 StopIteration 异常,则 pass 。

如果没有发生异常生成器方法将会执行 db.commit() ,否则会执行 db.rollback() 。

再次看看上述过程的代码大致实现:def contextmanager(func):

def helper(*args, **kwargs):

return GeneratorContextManager(func(*args, **kwargs))

return helper

class GeneratorContextManager(object):

def init(self, gen):

self.gen = gen

def enter(self):

try:

return self.gen.next()

except StopIteration:

raise RuntimeError("generator didn't yield")

def exit(self, type, value, traceback):

if type is None:

try:

self.gen.next()

except StopIteration:

pass

else:

raise RuntimeError("generator didn't stop")

else:

try:

self.gen.throw(type, value, traceback)

raise RuntimeError("generator didn't stop after throw()")

except StopIteration:

return True

except:

if sys.exc_info()[1] is not value:

raise

总结

Python的 with 表达式包含了很多Python特性。花点时间吃透 with 是一件非常值得的事情。

一些其他的例子

锁机制@contextmanager

def locked(lock):

lock.acquired()

try:

yield

finally:

lock.release()

标准输出重定向@contextmanager

def stdout_redirect(new_stdout):

old_stdout = sys.stdout

sys.stdout = new_stdout

try:

yield

finally:

sys.stdout = old_stdout

with open("file.txt", "w") as f:

with stdout_redirect(f):

print "hello world"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/539751.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

create table as select性能测试

转载自:http://blog.csdn.net/yangzhijun_cau/article/details/7396088 --------------------------------------------------------------------------------- 原表270W数据,无照片,字段比较多,有50个左右 测试机是一个虚拟机&a…

类似索引Model套Model之 iOS模型闲聊二

看下界面, 这是类似于索引的页面, 只不过木有右侧索引条的布局. 如果想了解通讯录索引的,请移步iOS - 高仿通讯录之商品索引排序搜索. 提供思路如下: 分析界面及接口用 MVC 设计模式来实现(其实核心点都在下面5)创建内外层 Model 并绑定两者 Model两者 Cell 布局的实现 (便于后…

输入法画面_搜狗输入法:用AI技术谱写诗意生活

十九世纪著名的思想家斯宾塞曾说:科学本身就富有诗意。这里应该包含两种意思,字面上,科学是饱含文字之美的,比如原理和规律的推演,仅通过文字符号的简单排列,便有了生机。但深层次上科学又不止于文字&#…

hadoop伪分布式(单机版)安装,Linux

一、下载 1、hadoop官网下载:https://archive.apache.org/dist/hadoop/common/ 进入stable文件夹里下载,这是稳定版本。 stable/ 本文的版本是 hadoop-2.7.2.tar.gz 2、jdk下载,JDK7及以上,本文用jdk8-64位 二、版本区别 2.…

c++-add two numbers 两个链表相加

题目描述 You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. Input: (2 -> 4 -> 3) (5 -> 6…

城轨的两类时钟系统均同步于_基于两台SDS3000示波器同步产生“8通道”示波器...

在很多应用场合需要4通道以上的示波器,但是市面上极大部分示波器最多只有四通道,而且没有外部输入的同步时钟接口。 有什么快捷的方法获得更多通道功能的示波器? 最简便的方法是:将两台示波器的辅助输入信号作为触发源,同时连接到…

Linux设置ssh免密码登录

一、SSH来源 对于需要远程管理其它机器,一般使用远程桌面或者telnet。linux一般只能是telnet。但是telnet的缺点是通信不加密,存在不安全因素,只适合内网访问。 为解决这个问题,推出了通信加密通信协议,即SSH&#x…

解析json数据_Retrofit同时解析JSON和XML数据格式

前言Android开发中,我们会经常遇到前端需要解析两种数据格式(json和xml),比如自己服务器返回的是json格式的数据,我们做微信登录的时候,微信返回的格式又是xml格式的。我们可以通过自己编写Retrofit的ConverterFactory来做到可以同时解析两种…

ORACLE 小时值必须介于1和12之间 解决方法

ORACLE数据库查询语句: "select * from dual where time>to_date(2012-10-29 19:45:34,yyyy-mm-dd HH:mi:ss)"当执行时,会抛出错误:ORA-01849: 小时值必须介于 1 和 12 之间 01849. 00000 - "hour must be between 1 and 1…

jenkins+svn+maven+ssh 部署配置详细记录

2019独角兽企业重金招聘Python工程师标准>>> 先简单记录一下,后面再慢慢完善。 1、环境 jdk 1.7.0_45 maven 3.1.1 jenkins 2.3.21 jdk和maven的安装就不必多说了,主要是jenkins的安装需要说下,jenkins有war包和yum还有rpm等安装方…

k8s安装sqlite3_kubernetes环境部署单节点redis数据库的方法

kubernetes部署redis数据库(单节点)redis简介Redis 是我们常用的非关系型数据库,在项目开发、测试、部署到生成环境时,经常需要部署一套 Redis 来对数据进行缓存。这里介绍下如何在 Kubernetes 环境中部署用于开发、测试的环境的 Redis 数据库&#xff0…

oracle 都是parallel惹的祸【1-2分钟出结果变1-2秒】

原文:http://blog.csdn.net/shushugood/article/details/9000628 -------------------------------------------------------- 该项目是中国联通xxxx话务系统,我的架构设计需求设计,运维保障数据库开发,全套服务。 在今天开发完毕…

二叉搜索树(BST树)的简单实现

#include <stdlib.h>template<typename T>class CBinSTree;template <typename T>class CTreeNode{//树节点类public:CTreeNode(const T& item,CTreeNode<T>* lptr NULL,CTreeNode<T>* rptr NULL):data(item),left(lptr),right(rptr){}CTr…

Oracle 创建 DBLink 的方法

原文出处&#xff1a;http://blog.csdn.net/davidhsing/article/details/6408770 ------------------- 1、如果需要创建全局 DBLink&#xff0c;则需要先确定用户有创建 dblink 的权限&#xff1a; [c-sharp] view plaincopy print?select * from user_sys_privs where privi…

eclipse init 配置

--设置最大的堆和最小堆大小.两者一样表示固定大小.这样可以防止老年代内存扩展造成额外的gc.当然也会多占一些内存.系统内存不足的慎用 -Xms512m -Xmx512m --加大年轻代内存.减少minor gc -Xmn164m --这个是永久代大小.默认是64M,增加到96M.固定大小,减少扩展造成的gc -XX:Per…

Oracle对表空间操作的sql

管理员给用户增加不限制表空间权限 grant unlimited tablespace to 用户; 查看表空间使用情况 SELECT a.tablespace_name "表空间名", total "表空间大小", free "表空间剩余大小", (total - free) "表空间使用大小", total / (…

IPKISS Tutorials------线路仿真

IPKISS------线路仿真 推荐阅读引言正文示例1------PDK中集成好的器件示例2------使用 i3.Circuit 框架示例3------i3.PCell 框架推荐阅读 Matplotlib ------ 纵坐标科学计数法含义 引言 我们知道,想要在 IPKISS 中进行仿真,首先需要对线路进行定义,但是我们知道,在 IPK…

Oracle Database 11g Express Edition使用限制,与其他版本的区别

Oracle Database 11g Express Edition是 Oracle 数据库的免费版本&#xff0c;支持标准版的大部分功能&#xff0c;11g Express Edition 提供 Windows 和 Linux 版本。 做为免费的 Oracle 数据库版本&#xff0c;Express Edition的限制是&#xff1a; 1&#xff09;最大数据库大…

c++ 复制构造函数_C++学习刷题8--复制构造函数和赋值运算符重载函数

一、前言本部分为C语言刷题系列中的第8节&#xff0c;主要讲解这几个知识点&#xff1a;复制构造函数和赋值运算符重载函数。欢迎大家提出意见、指出错误或提供更好的题目&#xff01;二、知识点讲解知识点1&#xff1a;复制构造函数1、当依据一个已存对象创建一个新对象时&…

ORACLE使用WITH AS和HINT MATERIALIZE优化SQL解决FILTER效率低下

原文&#xff1a;http://blog.csdn.net/liangweiwei130/article/details/37882503 ------------------------------------------------- 在做项目的过程中&#xff0c;一个页面使用类似如下的SQL查询数据&#xff0c;为了保密和使用方便&#xff0c;我把项目中有关的表名和字段…