Pergunta

É possível declarar mais de uma variável usando uma declaração with em Python?

Algo como:

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)

... ou está limpando dois recursos ao mesmo tempo o problema?

Foi útil?

Solução

É possível em Python 3 desde a v3.1 e < a href = "http://docs.python.org/dev/whatsnew/2.7.html#other-language-changes" rel = "noreferrer"> 2.7 Python . Os novos with sintaxe suporta vários gestores de contexto:

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

Ao contrário do contextlib.nested, este garante que a e b terá de chamada, mesmo se __exit__() ou do método C() levanta uma exceção a sua __enter__().

Outras dicas

contextlib.nested suporta o seguinte:

import contextlib

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

   ...

Update:
Para citar a documentação, em relação contextlib.nested :

Obsoleto desde a versão 2.7 : A-declaração com agora suporta este funcionalidade diretamente (sem erro confusas peculiaridades propensas).

de

Veja Rafal Dowgird resposta para obter mais informações.

Note que se você dividir as variáveis ??em linhas, você deve usar barras invertidas para embrulhar as novas linhas.

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

Parênteses não fazer o trabalho, já que Python cria uma tupla vez.

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

Desde tuplas não têm um atributo __enter__, você recebe um erro (undescriptive e não identifica o tipo de classe):

AttributeError: __enter__

Se você tentar as uso dentro de parênteses, Python pega o erro no momento da análise:

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

SyntaxError: inválido sintaxe

https://bugs.python.org/issue12782 parece estar relacionada a esta questão.

Eu acho que você quer fazer isso em vez disso:

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)

Desde Python 3.3, você pode usar a classe ExitStack do contextlib módulo.

Ele pode gerenciar um dinâmico número de objetos sensíveis ao contexto, o que significa que ele irá revelar-se especialmente útil se você não sabe quantos arquivos você está indo para alça.

O caso de uso canônico que é mencionado na documentação está a gerir um número dinâmico de arquivos.

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

Aqui está um exemplo genérico:

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]
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top