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访问mysql数据库的实现方法(2则示例)
Jan 06 Python
Python批量查询域名是否被注册过
Jun 21 Python
Python中的浮点数原理与运算分析
Oct 12 Python
浅谈numpy库的常用基本操作方法
Jan 09 Python
python实现关键词提取的示例讲解
Apr 28 Python
python Flask 装饰器顺序问题解决
Aug 08 Python
Python 实现OpenCV格式和PIL.Image格式互转
Jan 09 Python
python如何求100以内的素数
May 27 Python
python实现一次性封装多条sql语句(begin end)
Jun 06 Python
python else语句在循环中的运用详解
Jul 06 Python
简单了解Python字典copy与赋值的区别
Sep 16 Python
使用Python+OpenCV进行卡类型及16位卡号数字的OCR功能
Aug 30 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性能测试工具xhprof的详解
2013/06/03 PHP
PHP实现根据银行卡号判断银行
2015/04/29 PHP
Javascript Tab 导航插件 (23个)
2009/06/11 Javascript
jQuery+ajax实现顶一下,踩一下效果
2010/07/17 Javascript
用jquery实现的模拟QQ邮箱里的收件人选取及其他效果(一)
2011/01/06 Javascript
javascript eval(func())使用示例
2013/12/05 Javascript
jQuery ui 利用 datepicker插件实现开始日期(minDate)和结束日期(maxDate)
2014/05/22 Javascript
VS2008中使用JavaScript调用WebServices
2014/12/18 Javascript
javascript+html5实现仿flash滚动播放图片的方法
2015/04/27 Javascript
功能强大的Bootstrap效果展示(二)
2016/08/03 Javascript
AngularJS+bootstrap实现动态选择商品功能示例
2017/05/17 Javascript
js如何验证密码强度
2020/03/18 Javascript
vue实现短信验证码输入框
2020/04/17 Javascript
[20:21]《一刀刀一天》第十六期:TI国际邀请赛正式打响,总奖金超过550万
2014/05/23 DOTA
跟老齐学Python之for循环语句
2014/10/02 Python
Python优先队列实现方法示例
2017/09/21 Python
Django 2.0版本的新特性抢先看!
2018/01/05 Python
一些Centos Python 生产环境的部署命令(推荐)
2018/05/07 Python
PyTorch基本数据类型(一)
2019/05/22 Python
使用Python检测文章抄袭及去重算法原理解析
2019/06/14 Python
如何使用Python标准库进行性能测试
2019/06/25 Python
Python3.6+selenium2.53.6自动化测试_读取excel文件的方法
2019/09/06 Python
python实现UDP协议下的文件传输
2020/03/20 Python
python操作yaml说明
2020/04/08 Python
Python爬虫制作翻译程序的示例代码
2021/02/22 Python
澳大利亚首个在线预订旅游网站:Wotif
2017/07/19 全球购物
玩具公司的创业计划书
2013/12/31 职场文书
适用于所有创业者的创业计划书
2014/02/05 职场文书
学生会宣传部部长竞选演讲稿
2014/04/25 职场文书
我爱读书演讲稿
2014/05/07 职场文书
党员对十八届四中全会的期盼思想汇报范文
2014/10/17 职场文书
2015年度党风廉政建设工作情况汇报
2015/01/02 职场文书
幼儿园中班教育随笔
2015/08/14 职场文书
教师实习自我鉴定总结
2019/08/20 职场文书
delete in子查询不走索引问题分析
2022/07/07 MySQL
CSS list-style-type属性使用方法
2023/05/21 HTML / CSS