Python中的With语句的使用及原理


Posted in Python onJuly 29, 2020

总览

在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

到此这篇关于Python中的With语句的使用及原理的文章就介绍到这了,更多相关Python With语句内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python中分数的相关使用教程
Mar 30 Python
pandas 数据实现行间计算的方法
Jun 08 Python
python进行两个表格对比的方法
Jun 27 Python
详解python 注释、变量、类型
Aug 10 Python
python实现微信每日一句自动发送给喜欢的人
Apr 29 Python
python pandas写入excel文件的方法示例
Jun 25 Python
对Python中class和instance以及self的用法详解
Jun 26 Python
python读取并定位excel数据坐标系详解
Jun 26 Python
Django之创建引擎索引报错及解决详解
Jul 17 Python
python实现各种插值法(数值分析)
Jul 30 Python
tensorflow常用函数API介绍
Apr 19 Python
Python+Tkinter制作专属图形化界面
Apr 01 Python
解决c++调用python中文乱码问题
Jul 29 #Python
Python 实现简单的客户端认证
Jul 29 #Python
Tensorflow使用Anaconda、pycharm安装记录
Jul 29 #Python
学python爬虫能做什么
Jul 29 #Python
Python 创建TCP服务器的方法
Jul 28 #Python
Python实现画图软件功能方法详解
Jul 28 #Python
Python绘图之柱形图绘制详解
Jul 28 #Python
You might like
PHP下对数组进行排序的函数
2010/08/08 PHP
php判断访问IP的方法
2015/06/19 PHP
PHP实现ASCII码与字符串相互转换的方法
2017/04/29 PHP
ThinkPHP5.0 图片上传生成缩略图实例代码说明
2018/06/20 PHP
JavaScript 直接操作本地文件的实现代码
2009/12/01 Javascript
JS 表单验证大全
2011/11/23 Javascript
jQuery.getScript加载同域JS的代码
2012/02/13 Javascript
jquery实现图片左右切换的方法
2015/05/07 Javascript
Vue.js实战之组件之间的数据传递
2017/04/01 Javascript
MUI 实现侧滑菜单及其主体部分上下滑动的方法
2018/01/25 Javascript
JS中Object对象的原型概念基础
2018/01/29 Javascript
详解angular分页插件tm.pagination二次触发问题解决方案
2018/07/20 Javascript
vue-router之nuxt动态路由设置的两种方法小结
2018/09/26 Javascript
JS window对象简单操作完整示例
2020/01/14 Javascript
Vue.js获取手机系统型号、版本、浏览器类型的示例代码
2020/05/10 Javascript
js+canvas实现转盘效果(两个版本)
2020/09/13 Javascript
vue实现可移动的悬浮按钮
2021/03/04 Vue.js
[49:20]2014 DOTA2国际邀请赛中国区预选赛5.21 CIS VS TongFu
2014/05/22 DOTA
[00:43]魔廷新尊——痛苦女王至宝捆绑包
2020/06/12 DOTA
跟老齐学Python之??碌某?? target=
2014/09/12 Python
Python中的日期时间处理详解
2016/11/17 Python
对pandas进行数据预处理的实例讲解
2018/04/20 Python
pytorch对可变长度序列的处理方法详解
2018/12/08 Python
OpenCV-Python 摄像头实时检测人脸代码实例
2019/04/30 Python
给你一面国旗 教你用python画中国国旗
2019/09/24 Python
Django 批量插入数据的实现方法
2020/01/12 Python
利用pytorch实现对CIFAR-10数据集的分类
2020/01/14 Python
pytorch中使用cuda扩展的实现示例
2020/02/12 Python
宝塔面板成功部署Django项目流程(图文)
2020/06/22 Python
基于python爬取链家二手房信息代码示例
2020/10/21 Python
应聘编辑自荐信范文
2014/03/12 职场文书
2014法制宣传日活动总结范文
2014/11/01 职场文书
初三语文教学计划
2015/01/22 职场文书
2016大学军训通讯稿
2015/11/25 职场文书
解析laravel使用workerman用户交互、服务器交互
2021/04/28 PHP
pytorch 如何使用float64训练
2021/05/24 Python