当前位置: 首页 > 编程笔记 >

Python 的 with 语句详解

桓风史
2023-03-14
本文向大家介绍Python 的 with 语句详解,包括了Python 的 with 语句详解的使用技巧和注意事项,需要的朋友参考一下

一、简介

with是从Python 2.5 引入的一个新的语法,更准确的说,是一种上下文的管理协议,用于简化try…except…finally的处理流程。with通过__enter__方法初始化,然后在__exit__中做善后以及处理异常。对于一些需要预先设置,事后要清理的一些任务,with提供了一种非常方便的表达。

with的基本语法如下,EXPR是一个任意表达式,VAR是一个单一的变量(可以是tuple),”as VAR”是可选的。

with EXPR as VAR:
    BLOCK

根据PEP 343的解释,with…as…会被翻译成以下语句:
mgr = (EXPR)
exit = type(mgr).__exit__  # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

为什么这么复杂呢?注意finally中的代码,需要BLOCK被执行后才会执行finally的清理工作,因为当EXPR执行时抛出异常,访问mgr.exit执行就会报AttributeError的错误。


二、实现方式

根据前面对with的翻译可以看到,被with求值的对象必须有一个__enter__方法和一个__exit__方法。稍微看一个文件读取的例子吧,注意在这里我们要解决2个问题:文件读取异常,读取完毕后关闭文件句柄。用try…except一般会这样写:

f = open('/tmp/tmp.txt')
try:
    for line in f.readlines():
        print(line)
finally:
    f.close()

注意我们这里没有处理文件打开失败的IOError,上面的写法可以正常工作,但是对于每个打开的文件,我们都要手动关闭文件句柄。如果要使用with来实现上述功能,需要需要一个代理类:
class opened(object):

    def __init__(self, name):         self.handle = open(name)

    def __enter__(self):         return self.handle

    def __exit__(self, type, value, trackback):         self.handle.close()

with opened('/tmp/a.txt') as f:     for line in f.readlines():         print(line)


注意我们定了一个名字叫opened的辅助类,并实现了__enter__和__exit__方法,__enter__方法没有参数,__exit__方法的3个参数,分别代表异常的类型、值、以及堆栈信息,如果没有异常,3个入参的值都为None。

如果你不喜欢定义class,还可以用Python标准库提供的contextlib来实现:

from contextlib import contextmanager

@contextmanager def opened(name):     f = open(name)     try:         yield f     finally:         f.close()

with opened('/tmp/a.txt') as f:     for line in f.readlines():         print(line)


使用contextmanager的函数,yield只能返回一个参数,而yield后面是处理清理工作的代码。在我们读取文件的例子中,就是关闭文件句柄。这里原理上和我们之前实现的类opened是相同的,有兴趣的可以参考一下contextmanager的源代码。

三、应用场景
废话了这么多,那么到底那些场景下该使用with,有没有一些优秀的例子?当然啦,不然这篇文章意义何在。以下摘自PEP 343。

一个确保代码执行前加锁,执行后释放锁的模板:

@contextmanager
    def locked(lock):
        lock.acquire()
        try:
            yield
        finally:
            lock.release()

    with locked(myLock):         # Code here executes with myLock held.  The lock is         # guaranteed to be released when the block is left (even         # if via return or by an uncaught exception).


数据库事务的提交和回滚:
@contextmanager
        def transaction(db):
            db.begin()
            try:
                yield None
            except:
                db.rollback()
                raise
            else:
                db.commit()

重定向stdout:
@contextmanager
def stdout_redirected(new_stdout):
    save_stdout = sys.stdout
    sys.stdout = new_stdout
    try:
        yield None
    finally:
        sys.stdout = save_stdout

with opened(filename, "w") as f:     with stdout_redirected(f):         print "Hello world"


注意上面的例子不是线程安全的,再多线程环境中要小心使用。


四、总结

with是对try…expect…finally语法的一种简化,并且提供了对于异常非常好的处理方式。在Python有2种方式来实现with语法:class-based和decorator-based,2种方式在原理上是等价的,可以根据具体场景自己选择。

with最初起源于一种block…as…的语法,但是这种语法被很多人所唾弃,最后诞生了with,关于这段历史依然可以去参考PEP-343和PEP-340

 类似资料:
  • 问题内容: 我试图了解这些之间是否存在差异,以及该差异可能是什么。 选项一: 选项二: 我知道,对于选项一,with块之后处于闭合状态。 问题答案: 我不知道为什么没有人提到这一点,因为这是工作方式的基础。与Python中的许多语言功能一样,在后台调用特殊方法,这些方法已经为内置Python对象定义,并且可以由用户定义的类覆盖。在特定情况下(和上下文管理器),方法是和。 请记住,在Python中,

  • 本文向大家介绍理解Python中的With语句,包括了理解Python中的With语句的使用技巧和注意事项,需要的朋友参考一下 With语句是什么? 有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。 如果不用with语句,代码如下: 这里有两个问

  • 问题内容: 在python中创建多行的干净方法是什么?我想在一个内打开多个文件,但它足够靠右,足以在多行上显示。像这样: 不幸的是,这是一个。所以我尝试了这个: 也是语法错误。但是,这可行: 但是,如果我想发表评论怎么办?这不起作用: s的位置也没有任何明显的变化。 有没有一种干净的方法来创建允许多行注释的多行语句? 问题答案: 这在Python 3.9中已修复! https://github.c

  • 本文向大家介绍Python中的CSV文件使用"with"语句的方式详解,包括了Python中的CSV文件使用"with"语句的方式详解的使用技巧和注意事项,需要的朋友参考一下 是否可以直接使用with语句与CSV文件?能够做这样的事情似乎很自然: 但是csv.reader不提供__enter__和__exit__方法,所以这不行.但是我可以分两步做: 这是第二种方式吗?为什么他们不会使csv.re

  • 问题内容: 我今天是第一次遇到Python 语句。我已经使用Python几个月了,甚至不知道它的存在!考虑到它的地位有些晦涩,我认为值得一问: 语句旨在用于什么? 你用它来做什么? 我需要了解任何陷阱,还是与其使用相关的常见反模式?有什么比这更好用的情况吗? 为什么没有更广泛地使用它? 哪些标准库类与之兼容? 问题答案: 我相信这已经在我之前的其他用户那里得到了回答,因此我仅出于完整性的考虑而添加

  • 问题内容: 我喜欢Python 2.7中多上下文语句的便利性: 但是,我需要保持与2.6的兼容性。 是通过引入到2.5的,但是在文档中找不到关于多上下文版本被反向移植到2.6的任何信息。 有什么我想念的吗? 编辑:我知道可以嵌套语句。我在问是否可以使用多个with语句。 问题答案: 如果没有向后兼容的等效方法,我将使多上下文语句成为一组单上下文嵌套语句来处理。 编辑以解决您的修改: 如果您坚持不嵌