开发者社区> 马达达> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

Python 的 with 语句

简介:
+关注继续查看
qWxR2-Mbo6N8jKAoZQ65.jpg

With语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后关闭,线程中锁的获取和释放等。

术语

介绍一组与上下文管理器和with语句有关的概念。

上下文管理协议(Context Management Protocol):包含方法__enter__()和__exit__(),支持该协议的对象要实现这两个方法。
上下文管理器(Context Manager):支持上下文管理协议的对象,定义执行with语句时要建立的运行时上下文,负责执行with语句块上下文中的进入与退出操作。
运行时上下文(Runtime Context):由上下文管理器创建,通过上下文管理器的__enter__()和__exit__()方法实现。__enter__()方法在语句体执行之前进入运行时上下文;__exit__()在语句体执行完后从运行时上下文退出。
上下文表达式(Context Expression):with语句中关键字with的表达式,返回一个上下文管理器对象
语句体(With-Body):with语句下的代码块,在执行语句体之前会调用上下文管理器的__enter__()方法,执行完语句体之后会执行__exit__()方法。

基本语法

with语句的语法格式如下:

with context_expression [as target(s)]:
    with-body

假设对一个文件进行操作,使用with语句,代码如下:

with open(r'somefileName') as somefile:
    for line in somefile:
        print line
        # ...more code

不管在处理文件过程中是否发生异常,都能保证with语句执行完之后关闭文件句柄。如果使用传统的try/finally范式,则代码如下:

somefile = open(r'somefileName')try:
    for line in somefile:
        print line
        #...more codefinally:
    somefile.close()

相比较下,采用with语句可减少代码量。

工作原理

PEP 0343 对 with 语句的实现进行了描述。with 语句的执行过程类似如下代码块:

context_manager = context_expressionvalue = type(context_manager).__enter__(context_manager)exit = type(context_manager).__exit__exc = True # True 表示正常执行,即便有异常也忽略;False 表示重新抛出异常,需要对异常进行处理try:
    try:
        target = value # 如果使用了as子句
        with-body # 执行with-body
    except:
    # 执行过程中有异常发生
    exc = False
    # 如果 __exit__ 返回 True,则异常被忽略;如果返回 False,则重新抛出异常
    # 由外层代码对异常进行处理
    if not exit(context_manager, *sys.exc_info()):
        raisefinally:
    # 正常退出,或者通过 statement-body 中的 break/continue/return 语句退出
    # 或者忽略异常退出
    if exc:
        exit(context_manager, None, None, None)
    # 缺省返回 None,None 在布尔上下文中看做是 False
  1. 执行context_expression,生成上下文管理器context_manager

  2. 调用上下文管理器的__enter__()方法;若使用了as子句,则将__enter__()方法的返回值赋值给as子句中的target(s)。target(s)是单个变量,或者是元组(必须加“()”)

  3. 执行语句体with-body

  4. 不管执行过程中是否发生异常,都会执行上下文管理器的__exit__()方法。__exit__()方法负责执行“清理”工作,如释放资源等。若执行过程中没有出现异常,或者语句体with-body中执行语句 break/continue/return,则以 None 作为参数调用 __exit__(None, None, None);若执行过程中出现异常,则使用 sys.exc_info 得到的异常信息为参数调用__exit__(exc_type, exc_value, exc_traceback)

  5. 执行语句体with-body出现异常,若__exit__(exc_type, exc_value, exc_traceback)返回False,则重新抛出异常,with语句之外的逻辑处理异常;若返回True,则忽略此异常,不再对异常进行处理。

自定义上下文管理器

开发人员可以自定义支持上下文管理协议的类,实现上下文管理协议所需的两个方法:__exit__()和__exit__(exc_type, exc_value, exc_traceback)。
注意 上下文管理器必须同时提供 __enter__() 和__exit__() 方法的定义,缺少任何一个都会导致AttributeError;with 语句先检查是否定义了 __exit__() 方法,然后检查是否定义了__enter__()方法。
下面通过一个简单的示例来演示如何构建自定义的上下文管理器。

class DummyResource:
    def __init__(self, tag):
        self.tag = tag
        print('Resource[%s]' % tag)
    def __enter__(self):
        print('[Enter %s]: Allocate resource.' % self.tag)
        return self  # 可以返回不同的对象
    def __exit__(self, exc_type, exc_value, exc_tb):
        print('[Exit %s]: Free resource.' % self.tag)
        if exc_tb is None:
            print('[Exit %s]: Exited without exception.' % self.tag)
        else:
            print('[Exit %s]: Exited with exception raised.' % self.tag)
            return False  # 可以省略,缺省的None被看作是False

使用自定义的支持with语句的对象

with DummyResource('Normal'):
    print('[with-body] Run without exceptions.')

运行结果如下;

Resource[Normal][Enter Normal]: Allocate resource.[with-body] Run without exceptions.[Exit Normal]: Free resource.[Exit Normal]: Exited without exception.

正常执行时会先执行__enter__(),再执行完语句体 with-body,最后执行 __exit__()方法释放资源。

with DummyResource('With-Exception'):
    print('[with-body] Run with exception.')
    raise Exception
    print('[with-body] Run with exception. Failed to finish statement-body')

运行结果如下:

Resource[With-Exception][Enter With-Exception]: Allocate resource.[with-body] Run with exception.[Exit With-Exception]: Free resource.[Exit With-Exception]: Exited with exception raised.
Traceback (most recent call last):
  File "C:/DummyResource.py", line 23, in <module>
    raise Exception
Exception

Process finished with exit code 1

执行with-body语句发生异常,with-body语句体未执行完毕,但资源释放了,并抛出异常由with-body之外代码逻辑来捕获处理。

可以自定义上下文管理器来对软件系统中的资源进行管理,比如数据库连接、共享资源的访问控制等。Python 在线文档 Writing Context Managers 提供了一个针对数据库连接进行管理的上下文管理器的简单范例。

其他

特别感谢这篇文章浅谈 Python 的 with 语句,让我基本了解了with语句的用法。

8DXZKOqVOEWuJ4-qCcWu.jpg

原    文:TesterHome
作    者:mickey2017 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Python - with 语句
Python - with 语句
14 0
Python中的With语句
在Python中,您需要通过打开文件来访问文件。您可以使用 open()函数来实现。Open 返回一个文件对象,该文件对象具有用于获取有关已打开文件的信息和对其进行操作的方法和属性。
33 0
为什么Python没有属性赋值的“with”语句?
Python有一个 'with' 语句,它封装了块的执行,在块的入口和出口调用代码。有些语言的结构是这样的: a = 1 # equivalent to obj.a = 1 total = total + 1 # obj.total = obj.total + 1 在Python中,这样的结构是不明确的。
981 0
python的with语句,超级强大
With语句是什么? 有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。
600 0
理解Python的With语句
理解Python的With语句 Python’s with statement provides a very convenient way of dealing with the situation where you ...
831 0
《Python编程快速上手——让繁琐工作自动化》——1.5 第一个程序
本节书摘来自异步社区《Python编程快速上手——让繁琐工作自动化》一书中的第1章,第1.5节,作者[美] Al Sweigart,王海鹏 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.5 第一个程序 虽然交互式环境对于一次运行一条 Python指令很好,但要编写完整的Python程序,就需要在文件编辑器中输入指令。
1969 0
+关注
28
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载