“with”中的多个变量 陈述?

发布于 2024-07-21 00:48:39 字数 277 浏览 6 评论 0 原文

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

比如:

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)

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

Is it possible to declare more than one variable using a with statement in Python?

Something like:

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)

... or is cleaning up two resources at the same time the problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

笑叹一世浮沉 2024-07-28 00:48:39

Python 3 自 v3.1Python 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)

It is possible in Python 3 since v3.1 and Python 2.7. The new with syntax supports multiple context managers:

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

Unlike the contextlib.nested, this guarantees that a and b will have their __exit__()'s called even if C() or it's __enter__() method raises an exception.

You can also use earlier variables in later definitions (h/t Ahmad below):

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

As of Python 3.10, you can use parentheses:

with (
    A() as a, 
    B(a) as b, 
    C(a, b) as c,
):
    doSomething(a, c)
羁〃客ぐ 2024-07-28 00:48:39

请注意,如果将变量拆分为行,则在 Python 3.10 之前,必须使用反斜杠来换行。

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

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

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

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

属性错误:__enter__ 
  

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

with (A() as a,
      B() as b,
      C() as c):
    doSomething(a,b,c)
SyntaxError:语法无效 
  

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

此问题在 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。

Note that if you split the variables into lines, prior to Python 3.10 you must use backslashes to wrap the newlines.

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

Parentheses don't work, since Python creates a tuple instead.

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

Since tuples lack a __enter__ attribute, you get an error (undescriptive and does not identify class type):

AttributeError: __enter__

If you try to use as within parentheses, Python catches the mistake at parse time:

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

When will this be fixed?

This issue is tracked in https://bugs.python.org/issue12782.

Python announced in PEP 617 that they would replace the original parser with a new one. Because Python's original parser is LL(1), it cannot distinguish between "multiple context managers" with (A(), B()): and "tuple of values" with (A(), B())[0]:.

The new parser can properly parse multiple context managers surrounded by parentheses. The new parser has been enabled in 3.9. It was reported that this syntax will still be rejected until the old parser is removed in Python 3.10, and this syntax change was reported in the 3.10 release notes. But in my testing, it works in trinket.io's Python 3.9.6 as well.

恋竹姑娘 2024-07-28 00:48:39

contextlib.nested 支持此功能:

import contextlib

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

   ...

更新:
引用文档,关于 contextlib.nested< /a>:

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

有关更多信息,请参阅Rafał Dowgird 的回答

contextlib.nested supports this:

import contextlib

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

   ...

Update:
To quote the documentation, regarding contextlib.nested:

Deprecated since version 2.7: The with-statement now supports this
functionality directly (without the confusing error prone quirks).

See Rafał Dowgird's answer for more information.

情释 2024-07-28 00:48:39

从Python 3.3开始,您可以使用类 ExitStack 来自 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]

Since Python 3.3, you can use the class ExitStack from the contextlib module.

It can manage a dynamic number of context-aware objects, which means that it will prove especially useful if you don't know how many files you are going to handle.

The canonical use-case that is mentioned in the documentation is managing a dynamic number of files.

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

Here is a generic example:

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)

Output:

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]
小情绪 2024-07-28 00:48:39

我想你想这样做:

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)

I think you want to do this instead:

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)
我的黑色迷你裙 2024-07-28 00:48:39

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

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

From Python 3.10 there is a new feature of Parenthesized context managers, which permits syntax such as:

with (
    A() as a,
    B() as b
):
    do_something(a, b)
晨曦慕雪 2024-07-28 00:48:39

在 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)

In Python 3.1+ you can specify multiple context expressions, and they will be processed as if multiple with statements were nested:

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

is equivalent to

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

This also means that you can use the alias from the first expression in the second (useful when working with db connections/cursors):

with get_conn() as conn, conn.cursor() as cursor:
    cursor.execute(sql)
情深已缘浅 2024-07-28 00:48:39

您还可以单独创建上下文管理器(__init__方法)并输入上下文(__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 语句的标头中创建上下文管理器。

You can also separate creating a context manager (the __init__ method) and entering the context (the __enter__ method) to increase readability. So instead of writing this code:

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

you can write this code:

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

with company, person, vehicle:
    pass

Note that creating the context manager outside of the with statement makes an impression that the created object can also be further used outside of the statement. If this is not true for your context manager, the false impression may counterpart the readability attempt.

The documentation says:

Most context managers are written in a way that means they can only be used effectively in a with statement once. These single use context managers must be created afresh each time they’re used - attempting to use them a second time will trigger an exception or otherwise not work correctly.

This common limitation means that it is generally advisable to create context managers directly in the header of the with statement where they are used.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文