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中字典(Dictionary)用法实例详解
May 30 Python
剖析Python的Tornado框架中session支持的实现代码
Aug 21 Python
Python对list列表结构中的值进行去重的方法总结
May 07 Python
解决Python requests 报错方法集锦
Mar 19 Python
python多进程和多线程究竟谁更快(详解)
May 29 Python
python使用threading获取线程函数返回值的实现方法
Nov 15 Python
python使用xslt提取网页数据的方法
Feb 23 Python
Python基于pyCUDA实现GPU加速并行计算功能入门教程
Jun 19 Python
Python实现简单石头剪刀布游戏
Jan 20 Python
win10安装tesserocr配置 Python使用tesserocr识别字母数字验证码
Jan 16 Python
Python开发之身份证验证库id_validator验证身份证号合法性及根据身份证号返回住址年龄等信息
Mar 20 Python
tensorflow dataset.shuffle、dataset.batch、dataset.repeat顺序区别详解
Jun 03 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 程序员的调试技术小结
2009/11/15 PHP
php读取大文件示例分享(文件操作类)
2014/04/13 PHP
php多重接口的实现方法
2015/06/20 PHP
PHP结合Redis+MySQL实现冷热数据交换应用案例详解
2019/07/09 PHP
静态页面的值传递(三部曲)
2006/09/25 Javascript
js trim函数 去空格函数与正则集锦
2009/11/20 Javascript
js写一个字符串转成驼峰的实例
2013/06/21 Javascript
简单js代码实现selece二级联动(推荐)
2014/02/18 Javascript
Javascript基础教程之JavaScript语法
2015/01/18 Javascript
基于JS代码实现图片在页面中旋转效果
2016/06/16 Javascript
轻松实现jquery选项卡切换效果
2016/10/10 Javascript
一次记住JavaScript的6个正则表达式方法
2018/02/22 Javascript
ES6关于Promise的用法详解
2018/05/07 Javascript
详解webpack的proxyTable无效的解决方案
2018/06/15 Javascript
解决LayUI加上form.render()下拉框和单选以及复选框不出来的问题
2019/09/27 Javascript
详解Vue3中对VDOM的改进
2020/04/23 Javascript
[03:52]显微镜下的DOTA2第三期——英雄在无聊的时候干什么
2014/06/20 DOTA
配置 Pycharm 默认 Test runner 的图文教程
2018/11/30 Python
pyqt5中QThread在使用时出现重复emit的实例
2019/06/21 Python
如何理解python面向对象编程
2020/06/01 Python
Python入门基础之数字字符串与列表
2021/02/01 Python
html5使用Drag事件编辑器拖拽上传图片的示例代码
2017/08/22 HTML / CSS
美国最便宜的旅游网站:CheapTickets
2017/07/09 全球购物
Turnbull & Asser官网:英国皇室御用的顶级定制衬衫
2019/01/31 全球购物
大学生毕业求职的自我评价
2013/09/29 职场文书
学院书画协会部门职责
2013/11/28 职场文书
大专毕业自我鉴定
2014/02/04 职场文书
中式婚礼主持词
2014/03/13 职场文书
秋天的图画教学反思
2014/05/01 职场文书
献爱心大型公益活动策划方案
2014/09/15 职场文书
初三英语教学计划
2015/01/23 职场文书
出纳岗位职责
2015/01/31 职场文书
个人简历求职信范文
2015/03/20 职场文书
2015年幼儿园中班开学寄语
2015/05/27 职场文书
Python如何识别银行卡卡号?
2021/06/10 Python
Redis 操作多个数据库的配置的方法实现
2022/03/23 Redis