“with”语句中有多个变量吗?

2025-03-05 09:16:00
admin
原创
85
摘要:问题描述:with是否可以使用Python 中的语句声明多个变量?类似于:from __future__ import with_statement with open("out.txt","wt"), open("in.txt") as file...

问题描述:

with是否可以使用Python 中的语句声明多个变量?

类似于:

from __future__ import with_statement

with open("out.txt","wt"), open("in.txt") as file_out, file_in:
    for line in file_in:
        file_out.write(line)

...或者同时清理两个资源是个问题吗?


解决方案 1:

从 Python 3 v3.1 开始以及 Python 2.7中都可以实现。新with语法支持多个上下文管理器:

with A() as a, B() as b, C() as c:
    doSomething(a,b,c)

与 不同contextlib.nested,这保证了即使或 它的方法引发异常,ab也会调用它们的。__exit__()`C()`__enter__()

您还可以在后续定义中使用早期的变量(h/t Ahmad如下):

with A() as a, B(a) as b, C(a, b) as c:
    doSomething(a, c)

从 Python 3.10 开始,你可以使用括号:

with (
    A() as a, 
    B(a) as b, 
    C(a, b) as c,
):
    doSomething(a, c)

解决方案 2:

请注意,如果将变量分成几行,在 Python 3.10 之前版本中,必须使用反斜杠来包装换行符。

with A() as a, \n     B() as b, \n     C() as c:
    doSomething(a,b,c)

括号不起作用,因为 Python 会创建一个元组。

with (A(),
      B(),
      C()):
    doSomething(a,b,c)

由于元组缺少__enter__属性,因此会收到错误(不具描述性且无法识别类类型):

AttributeError: __enter__

如果你尝试在括号内使用as,Python 会在解析时捕获错误:

with (A() as a,
      B() as b,
      C() as c):
    doSomething(a,b,c)
SyntaxError: invalid syntax

什么时候能修复这个问题?

该问题已在https://bugs.python.org/issue12782中跟踪。

Python 在PEP 617中宣布将用新解析器替换原有的解析器。因为 Python 的原有解析器是 LL(1),它无法区分“多个上下文管理器”with (A(), B()):和“值元组” with (A(), B())[0]:

新的解析器可以正确解析括号内的多个上下文管理器。新的解析器已在 3.9 中启用。据报道,在 Python 3.10 中移除旧解析器之前,此语法仍将被拒绝,并且此语法更改已在3.10 发行说明中报告。但在我的测试中,它在 trinket.io 的 Python 3.9.6 中也能正常工作。

解决方案 3:

contextlib.nested支持这一点:

import contextlib

with contextlib.nested(open("out.txt","wt"), open("in.txt")) as (file_out, file_in):

   ...

更新:

引用文档,关于contextlib.nested

自 2.7 版起已弃用:with 语句现在直接支持此功能(没有令人困惑的容易出错的怪癖)。

请参阅Rafał Dowgird 的回答以了解更多信息。

解决方案 4:

ExitStack从 Python 3.3 开始,您可以使用模块中的类contextlib

它可以管理动态数量的上下文感知对象,这意味着如果您不知道要处理多少个文件,它将特别有用。

文档中提到的规范用例是管理动态数量的文件。

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    # All opened files will automatically be closed at the end of
    # the with statement, even if attempts to open files later
    # in the list raise an exception

以下是一个通用的例子:

from contextlib import ExitStack

class X:
    num = 1

    def __init__(self):
        self.num = X.num
        X.num += 1

    def __repr__(self):
        cls = type(self)
        return '{cls.__name__}{self.num}'.format(cls=cls, self=self)

    def __enter__(self):
        print('enter {!r}'.format(self))
        return self.num

    def __exit__(self, exc_type, exc_value, traceback):
        print('exit {!r}'.format(self))
        return True

xs = [X() for _ in range(3)]

with ExitStack() as stack:
    print(stack._exit_callbacks)
    nums = [stack.enter_context(x) for x in xs]
    print(stack._exit_callbacks)
print(stack._exit_callbacks)
print(nums)

输出:

deque([])
enter X1
enter X2
enter X3
deque([<function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86158>, <function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f861e0>, <function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86268>])
exit X3
exit X2
exit X1
deque([])
[1, 2, 3]

解决方案 5:

我认为你应该这样做:

from __future__ import with_statement

with open("out.txt","wt") as file_out:
    with open("in.txt") as file_in:
        for line in file_in:
            file_out.write(line)

解决方案 6:

从 Python 3.10 开始,有一个新功能,即括号上下文管理器,它允许如下语法:

with (
    A() as a,
    B() as b
):
    do_something(a, b)

解决方案 7:

在 Python 3.1+ 中,您可以指定多个上下文表达式,它们将被处理,就像with嵌套多个语句一样:

with A() as a, B() as b:
    suite

相当于

with A() as a:
    with B() as b:
        suite

这也意味着您可以在第二个表达式中使用第一个表达式的别名(在使用数据库连接/游标时很有用):

with get_conn() as conn, conn.cursor() as cursor:
    cursor.execute(sql)

解决方案 8:

您还可以将创建上下文管理器(方法)和进入上下文(方法)分开,以提高可读性。因此,不要编写以下代码:__init__`__enter__`

with Company(name, id) as company, Person(name, age, gender) as person, Vehicle(brand) as vehicle:
    pass

你可以写这样的代码:

company = Company(name, id)
person = Person(name, age, gender)
vehicle = Vehicle(brand)

with company, person, vehicle:
    pass

请注意,在语句之外创建上下文管理器with会给人留下这样的印象:创建的对象也可以在语句之外进一步使用。如果您的上下文管理器并非如此,则错误的印象可能会影响可读性。

文档说:

大多数上下文管理器的编写方式意味着它们只能在 with 语句中有效使用一次。这些一次性上下文管理器必须在每次使用时重新创建 - 尝试第二次使用它们将触发异常或无法正常工作。

这种常见的限制意味着通常建议直接在使用它们的 with 语句的标题中创建上下文管理器。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3958  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2737  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   74  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   83  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   70  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用