关于python:’with’语句中有多个变量?

Multiple variables in a 'with' statement?

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

就像是:

1
2
3
4
5
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)

......还是正在清理两个资源同时出现问题?


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

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

contextlib.nested不同,这保证即使C()__enter__()方法引发异常,ab也会调用__exit__()


contextlib.nested支持这个:

1
2
3
4
5
import contextlib

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

   ...

更新:
引用文档,关于contextlib.nested

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

看拉法? Dowgird的回答是为了获取更多信息。


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

1
2
3
4
with A() as a, \
     B() as b, \
     C() as c:
    doSomething(a,b,c)

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

1
2
3
4
with (A(),
      B(),
      C()):
    doSomething(a,b,c)

由于元组缺少__enter__属性,因此会出现错误(无法识别并且不识别类类型):

1
AttributeError: __enter__

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

1
2
3
4
with (A() as a,
      B() as b,
      C() as c):
    doSomething(a,b,c)

SyntaxError: invalid syntax

https://bugs.python.org/issue12782似乎与此问题有关。


我想你想这样做:

1
2
3
4
5
6
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)


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

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

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

1
2
3
4
5
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

这是一个通用的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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)

输出:

1
2
3
4
5
6
7
8
9
10
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]