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参数和作用域的使用
Nov 01 Python
零基础写python爬虫之爬虫框架Scrapy安装配置
Nov 06 Python
Python格式化css文件的方法
Mar 10 Python
python 实现tar文件压缩解压的实例详解
Aug 20 Python
Python使用百度翻译开发平台实现英文翻译为中文功能示例
Aug 08 Python
Python装饰器原理与基本用法分析
Jan 07 Python
Python2与Python3的区别详解
Feb 09 Python
python实现梯度下降法
Mar 24 Python
python os模块常用的29种方法使用详解
Jun 02 Python
详解python对象之间的交互
Sep 29 Python
总结python 三种常见的内存泄漏场景
Nov 20 Python
详解python的异常捕获
Mar 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并发多进程处理利器Gearman使用介绍
2016/05/16 PHP
Yii2实现log输出到file及database的方法
2016/11/12 PHP
php实现的二分查找算法示例
2017/06/20 PHP
PHP实现的微信APP支付功能示例【基于TP5框架】
2019/09/16 PHP
div当滚动到页面顶部的时候固定在顶部实例代码
2013/05/27 Javascript
Jquery实现自定义窗口随意的拖拽
2014/03/12 Javascript
自己使用jquery写的一个无缝滚动的插件
2014/04/30 Javascript
Node.js文件操作详解
2014/08/16 Javascript
JavaScript使用function定义对象并调用的方法
2015/03/23 Javascript
jQuery判断多个input file 都不能为空的例子
2015/06/23 Javascript
js实现图片上传并正常显示
2015/12/19 Javascript
jQuery图片旋转插件jQueryRotate.js用法实例(附demo下载)
2016/01/21 Javascript
JS控制静态页面传递参数并获取参数应用
2016/08/10 Javascript
jQuery实现优雅的弹窗效果(6)
2017/02/08 Javascript
微信小程序实现缓存根据不同的id来进行设置和读取缓存
2017/06/12 Javascript
JavaScript基于扩展String实现替换字符串中index处字符的方法
2017/06/13 Javascript
微信小程序调用摄像头隐藏式拍照功能
2018/08/22 Javascript
js限制输入框只能输入数字(onkeyup触发)
2018/09/28 Javascript
Vue可自定义tab组件用法实例
2019/10/24 Javascript
[01:11:10]2014 DOTA2华西杯精英邀请赛 5 24 iG VS VG加赛
2014/05/26 DOTA
探究Python多进程编程下线程之间变量的共享问题
2015/05/05 Python
python中根据字符串调用函数的实现方法
2016/06/12 Python
python模块之time模块(实例讲解)
2017/09/13 Python
keras模型可视化,层可视化及kernel可视化实例
2020/01/24 Python
详解CSS3 用border写 空心三角箭头 (两种写法)
2017/09/29 HTML / CSS
基于HTML5 Canvas 实现弹出框效果
2017/06/05 HTML / CSS
浅谈利用缓存来优化HTML5 Canvas程序的性能
2015/05/12 HTML / CSS
Zadig&Voltaire官网:法国时装品牌
2018/01/05 全球购物
网络通讯中,端口有什么含义,端口的取值范围
2012/11/23 面试题
GMP办公室主任岗位职责
2014/03/14 职场文书
如何写求职信
2014/05/24 职场文书
国家领导干部党的群众路线教育实践活动批评与自我批评材料
2014/09/23 职场文书
军人离婚协议书样本
2014/10/21 职场文书
埃及王子观后感
2015/06/16 职场文书
html+css实现滚动到元素位置显示加载动画效果
2021/08/02 HTML / CSS
码云(gitee)通过git自动同步到阿里云服务器
2022/12/24 Servers