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


391

使用withPython中的语句可以声明多个变量吗?

就像是:

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)

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


可能是这样的:将[expr1,expr2]设为f :,然后使用f [0]和f [1]。
jbasko

本来是很高兴,因为没有必要进口的东西....但它不工作AttributeError的:“名单”对象有没有属性“ 退出
河豚

如果python仅包含闭包,则不需要with语句
BT

不需要使用with语句,对吗?您可以将file_out和file_in设置为None,然后进行try / except / finally,在其中打开它们并在try中对其进行处理,如果它们不是None,则最后关闭它们。不需要双重缩进。
M Katz 2013年

1
这些答案中有许多并没有涉及使用两个以上语句的需求。从理论上讲,可能有需要打开数十个上下文的应用程序,如果施加了任何行长限制,嵌套就会很快崩溃。
ThorSummoner 2014年

Answers:


666

从v3.1Python 2.7 开始,Python 3中是可能的。新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)

1
是否有可能在with语句中将字段设置为等于with open('./file') as arg.x = file:
查理·帕克

13
此外,还有可能:A()为a,B(a)为b,C(a,b)为c:
Ahmad Yoosofan

class test2:x = 1;t2 = test2(),其中open('f2.txt')为t2.x:对于t2.x中的l1,readlines():print(l1);
#Charlie

1
请注意,as是可选的。
斯瓦沃米尔Lenart

为了澄清@SławomirLenart是说:as如果你需要的对象是必需的ab,但整体as a还是as b不需要
西普里安Tomoiagă

56

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的答案


34
遗憾的是,但是我认为nested上下文管理器是一个错误,永远不要使用。在此示例中,如果打开第二个文件引发异常,则第一个文件将完全不会关闭,从而完全破坏了使用上下文管理器的目的。
2009年

为什么这么说 该文档说,使用嵌套等同于嵌套“与”
James Hopkin

@Rafal:浏览手册似乎表明python正确嵌套了with语句。真正的问题是第二个文件在关闭时是否引发异常。
未知

10
@詹姆斯:否,在该文档的等效代码docs.python.org/library/contextlib.html#contextlib.nested从标准嵌套不同with块。进入with块之前按顺序创建管理器:m1,m2,m3 = A(),B(),C()如果B()或C()异常失败,那么您唯一希望正确完成A( )是垃圾收集器。
2009年

8
自2.7版起不推荐使用。注意:with语句现在直接支持此功能(而不会产生容易出错的错误)。
2013年

36

请注意,如果将变量分成几行,则必须使用反斜杠来包装换行符。

with A() as a, \
     B() as b, \
     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:语法无效

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


16

我认为您想这样做:

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)

5
这就是我目前的做法,但是嵌套的深度是我想要的(意味着)它的深度的两倍...
河豚鱼

我认为这是最干净的方法-任何其他方法都将更难阅读。Alex Martelli的答案似乎更接近您想要的答案,但可读性却差得多。为什么嵌套如此关注?
安德鲁·黑尔

7
诚然,这没什么大不了的,但是,每“导入一个”(又称“ Python的禅”),“扁平比嵌套好”-这就是为什么我们将contextlib.nested添加到标准库中的原因。BTW,3.1可能具有新语法“将A()作为a,将B()作为b:”(此修补程序已发布,到目前为止,尚无BDFL声明),以便获得更直接的支持(显然,库解决方案是“被认为是完美的……但是避免不必要的嵌套无疑是Python核心开发人员广泛的共同目标。
Alex Martelli

2
@Alex:非常正确,但我们还必须考虑“可读性很重要”。
安德鲁·黑尔

4
@Andrew:我认为缩进的一个层次更好地表达了程序的预期逻辑,即“原子地”创建两个变量,然后一起清理它们(我意识到这实际上没有发生)。认为异常问题虽然会破坏交易
河豚鱼

12

因为Python 3.3,你可以使用类ExitStackcontextlib模块。

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

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

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]

0

在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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.