Python中的With语句的使用及原理
作者:Yujiaao 发布时间:2023-03-16 01:01:23
总览
在Python中,您需要通过打开文件来访问文件。您可以使用 open()函数来实现。Open 返回一个文件对象,该文件对象具有用于获取有关已打开文件的信息和对其进行操作的方法和属性。
with 语句
使用 “with” 语句,使代码简洁,处理异常也更优雅。
“with语句通过封装常用的准备工作和清除任务来简化异常处理。”
此外,它将自动关闭文件。with 语句提供了一种确保始终使用清理的方法。
如果没有 with 语句,我们将编写如下内容:
file = open("welcome.txt")
data = file.read()
print(data)
file.close() # 文件用完一定要关闭
with 语句用法
'with' 语句是一个新的控制流结构,其基本结构为:
with expression [as variable]:
with-block
使用 with 打开文件非常简单:使用open(filename) as file:
with open("welcome.txt") as file: # file 做为对文件对象的引用
data = file.read()
# 使用 data 做点啥
在写入模式下打开output.txt
with open('output.txt', 'w') as file: # 输出到file
file.write('Hi there!')
注意,我们不必编写 file.close()。会被自动调用。
原理
' with '语句简化了以前使用try...finally块来确保执行清除代码的代码。在本节中,我将讨论通常使用的语句。在下一节中,我将检查实现细节,并说明如何编写用于此语句的对象。
with 后面的表达式需支持上下文管理协议 (即,__enter__() 和__exit__() 方法)。
with expression [as variable]:
with-block
在执行 with-block 之前调用对象的__enter __() 方法,因此可以运行setup设置代码。可以能过 as 把表达式结果绑定到变量 variable(注意这里不是赋值到变量 variable)。
with 块的执行完成后,即使该块引发了异常,该对象的 __exit__() 方法也会被调用,因此可以运行清理代码。
要在Python 2.5中启用该语句,您需要在模块中添加以下指令:
from __future__ import with_statement
该语句将始终在 Python 2.6 中启用。
现在,一些标准的 Python 对象支持上下文管理协议,并且可以与 'with' 语句一起使用。文件对象即是其中之一:
with open('/etc/passwd', 'r') as f:
for line in f:
print line
... 更多 ...
执行此语句后,即使for循环在代码块中途出现异常,f中的文件对象也将自动关闭。
注意: 在这种情况下,f 是 open() 创建的同一对象 ,因为 file.__enter__()返回 self。
threading 模块的锁和条件变量也支持 'with' 语句:
lock = threading.Lock()
with lock:
# 代码临界区
...
该锁在执行 with 块之前获取,并在该块完成后始终释放。
decimal模块中 的新 localcontext() 函数使保存和还原当前decimal上下文变得容易,它封装了计算所需的精度和舍入特征:
from decimal import Decimal, Context, localcontext
# 显示默认精度: 28 位数字
v = Decimal('578')
print v.sqrt()
with localcontext(Context(prec=16)):
# 本代码块中使用16位精度.
# 原始上下文将在退出块后恢复.
print(v.sqrt())
编写上下文管理器
在幕后,with 语句相当复杂。大多数人只会在与现有对象一起使用 'with',并且不需要知道这些详细信息,如果您想让自己写的类也支持 with语句,那就需要了解上下文管理器了。
上下文管理协议的高级解释是:
该表达式将被求值并应产生一个称为``context manager''的对象。上下文管理器必须包含 __enter__() 和 __exit__() 方法。
上下文管理器的 __enter__() 方法被调用。返回的值分配给 var 。如果不存在as var子句,则仅丢弃该值。
with 块中的代码被执行。
如果 with 块引发异常, 则使用异常详细信息调用__exit__(type,value,traceback),该异常详细信息由sys.exc_info() 返回 。该方法的返回值控制是否重新引发异常:任何 False 值都会重新引发异常,True会抑制异常。通常很少需要抑制异常,因为如果您这样做,包含 'with' 语句的代码的作者将永远不会意识到任何错误。
如果 with 块没有引发异常,则仍然会调用__exit__()方法,此时参数type,value和traceback都是 None。
让我们考虑一个例子。我不会提供详细的代码,而只会概述支持事务的数据库所必需的方法。
(对于不熟悉数据库术语的人:将对数据库的一组更改分组为一个事务。可以提交事务,这意味着将所有更改都写入数据库,也可以回滚,这意味着将所有更改都丢弃并删除。数据库未更改。有关更多信息,请参见任何数据库教科书。)
假设有一个代表数据库连接的对象。我们的目标是让用户编写如下代码:
db_connection = DatabaseConnection()
with db_connection as cursor:
cursor.execute('insert into ...')
cursor.execute('delete from ...')
# ... more operations ...
如果块中的代码完美运行,则应该提交事务;如果有异常,则应回滚事务。这是我假设的DatabaseConnection的基本接口:
class DatabaseConnection:
...
def __enter__ (self):
# Code to start a new transaction
cursor = self.cursor()
return cursor
该__enter __()方法是很简单的,只有到启动新的事务。对于此应用程序,结果光标对象将是有用的结果,因此该方法将返回它。然后,用户可以添加as cursor到其 with 语句中,以将游标绑定到变量名。
class DatabaseConnection:
# Database interface
def cursor (self):
"Returns a cursor object and starts a new transaction"
def commit (self):
"Commits current transaction"
def rollback (self):
"Rolls back current transaction"
该__exit __()方法有点复杂,该方法必须检查是否发生异常。如果没有异常,则提交事务。如果存在异常,则事务将回滚。
在下面的代码中,执行会从函数的末尾开始,并返回默认值None。 None为假,因此将自动重新引发异常。如果需要,可以更加明确,并 在标记的位置添加return语句。
class DatabaseConnection:
...
def __exit__ (self, type, value, tb):
if tb is None:
# No exception, so commit
self.commit()
else:
# Exception occurred, so rollback.
self.rollback()
# return False
contextlib 模块
contextlib 模块提供了一些功能和装饰器,这些功能和装饰器对于编写与 'with' 语句一起使用的对象很有用。
装饰器称为 contextmanager,它使您可以编写一个生成器函数,而不用定义一个新类。生成器应恰好产生一个值。直到yield的代码 将作为__enter __()方法执行,并且yield的值将是该方法的返回值,该返回值将绑定到' with '语句的as子句中的变量(如果有)。屈服后的代码将在 __exit __()方法中执行。块中引发的任何异常都将由yield语句引发。
上一节中的数据库示例可以使用以下装饰器编写为:
from contextlib import contextmanager
@contextmanager
def db_transaction (connection):
cursor = connection.cursor()
try:
yield cursor
except:
connection.rollback()
raise
else:
connection.commit()
db = DatabaseConnection()
with db_transaction(db) as cursor:
该contextlib模块还具有嵌套(MGR1, MGR2,...)功能结合了一些上下文管理器,所以你不需要写嵌套“不与 ”语句。在此示例中,单个' with '语句既启动数据库事务并获取线程锁:
lock = threading.Lock()
with nested (db_transaction(db), lock) as (cursor, locked):
最后,Closeing(object)函数返回object,以便可以将其绑定到变量,并object.close()在块的末尾调用。
import urllib, sys
from contextlib import closing
with closing(urllib.urlopen('http://bixuebihui.com')) as f:
for line in f:
sys.stdout.write(line)
参考:
https://docs.python.org/2.5/whatsnew/pep-343.html
来源:https://segmentfault.com/a/1190000023415387


猜你喜欢
- Python是静态作用域语言,尽管它自身是一个动态语言。也就是说,在Python中变量的作用域是由它在源代码中的位置决定的,这与C有些相似,
- 前言hello,大家好,学习一段时间了,学习了框架和后台的内容,为了防止前端的js和jq的熟练度不够,忘记很多算法和基础用法,会陆陆续续更新
- 前言这篇文章主要给大家总结了关于学习Python的新手们容易犯的几个错误,一共四个易犯错误,下面来看看详细的介绍吧。一、i+=1 不等于++
- 我们都知道并发(不是并行)编程目前有四种方式,多进程,多线程,异步,和协程。多进程编程在python中有类似C的os.fork,当然还有更高
- ThinkPHP提供的视图查询应用功能十分强大,用户利用视图查询功能可以将多个数据表的字段内容按需要进行指定和筛选,组织成一个基于这些数据表
- 以上是开头,安装完后需要导入转载的代码读取所有docx文件中的内容发现没有读取到表格数据:from docx import Document
- BN与Dropout共同使用出现的问题BN和Dropout单独使用都能减少过拟合并加速训练速度,但如果一起使用的话并不会产生1+1>2
- 本文实例为大家分享了JavaScript实现简易轮播图效果的具体代码,供大家参考,具体内容如下全部代码:<!DOCTYPE html&
- 最近好多伙伴说,我用vue做的项目本地是可以的,但部署到服务器遇到好多问题:资源找不到,直接访问index.html页面空白,刷新当前路由4
- 问题Vue项目中需要用Echarts的柱状图显示数据,并且每次搜索要更新柱状图。这时候小编发现在控制台会出现这样的报错:原来的代码是这样的,
- --Create Company Table Create Table Company ( ComID varchar(50) primar
- 平台:windows 10pycharm 2016.2python 2.7.12问题始于我在pycharm下建了一个flask工程,然后导入
- 其实网上有很多关于python2.6.6 升级到python2.7的文章,但是我对比这些类似的文章升级之后,发现其中有错误的地方,于是决定还
- 代码如下:'============================== '格式化HTML,SDCMS加强版 '==
- 今天比较忙,水一下下面的代码来源于这个视频里面提到的,github 的链接为:github.com/mikeckenned…(本地下载)第一
- 虽然Python被说成是一种解释型语言,但是实际上,Python源程序要先经过编译,然后才能运行。与Java语言类似,Python源程序编译
- 您是否常常在做网页的过程中发现一个问题呢?当图片上传的时候,如果图片太大 ,就会把网页撑破,唯一做的就要先把它用软件缩小,再上传上
- 将.py文件转化为.exe文件首先需要第三方库 pyinstaller1.如果没有安装pyinstaller,则在命令提示符输入 pip i
- 介绍Go使用goroutines来处理connection的读写事件,不会阻塞:c, err := srv.newConn(rw) &nbs
- 我需要查询从现在算起五天前的日期。按照商业习惯,这五天应该不包含星期六和星期天。专家回答:对于许多跟商业日期有关的情况,最好的解决方案是使用