Variável definida com declaração with disponível fora de with-block?

88

Considere o seguinte exemplo:

with open('a.txt') as f:
    pass
# Is f supposed to be defined here?

Eu li a documentação da linguagem (2.7) para with-statement e também para PEP-343, mas pelo que eu posso dizer eles não dizem nada sobre este assunto.

No CPython 2.6.5 fparece estar definido fora do bloco com, mas prefiro não confiar em um detalhe de implementação que possa mudar.

Heikki Toivonen
fonte
8
A pergunta se f estaria ou não disponível no escopo anexo já foi respondida. Para mim, todo o conceito de gerenciadores de contexto clicou quando percebi que o conceito de contexto é diferente daquele de escopo . Aqui está um link para o meu site que espero ajudar um pouco: markus-gattol.name/ws/python.html#context_manager
Tom
1
Exatamente - um contexto é uma questão de mudar o estado atual - arquivo aberto, arquivo fechado ou thread bloqueado / desbloqueado. Dispositivo alocado / desalocado. Todas as variáveis ​​nomeadas no escopo ainda estão lá - mas agora elas apontarão para identificadores desalocados / fechados / desbloqueados.
Danny Staple

Respostas:

158

Sim, o gerenciador de contexto estará disponível fora da instrução with e isso não depende da implementação ou da versão. com instruções não criam um novo escopo de execução.

fuzzyman
fonte
3
Esta é a explicação mais clara em minha opinião, concedendo assim a resposta aceita; dará pontos a Alex e TokenMacGuy para informações úteis adicionais.
Heikki Toivonen
Algo que alguém pode facilmente esquecer se não trabalhar com Python por um tempo, a função como indentação, nome e outras coisas sugere que você não deveria ser capaz de acessá-la, mas pode.
Vitaliy Terziev
28

a withsintaxe:

with foo as bar:
    baz()

é aproximadamente açúcar para:

try:
    bar = foo.__enter__()
    baz()
finally:
    if foo.__exit__(*sys.exc_info()) and sys.exc_info():
        raise

Isso geralmente é útil. Por exemplo

import threading
with threading.Lock() as myLock:
    frob()

with myLock:
    frob_some_more()

o gerenciador de contexto pode ser útil mais de uma vez.

SingleNegationElimination
fonte
Bem, a reutilização de bloqueio pode ou não (não tenho ideia, mas seria um bug se fossem diferentes) - mas as regras de escopo do Python definitivamente serão as mesmas aqui em todas as implementações.
fuzzyman
1
novamente, este não é um problema de escopo. O escopo será o mesmo. No entanto, se a implementação de foo .__ exit__ coloca a thread em um estado interrompido, então, a menos que lock tenha um enter que o bloqueia novamente, a segunda instrução não parece que faria algo útil para os bloqueios de thread.
Danny Staple
16

Caso fseja um arquivo, ele aparecerá fechado fora do withextrato.

Por exemplo, este

f = 42
print f
with open('6432134.py') as f:
    print f
print f

imprimiria:

42
<open file '6432134.py', mode 'r' at 0x10050fb70>
<closed file '6432134.py', mode 'r' at 0x10050fb70>

Você pode encontrar os detalhes no PEP-0343 na seção Especificação: A declaração 'com' . As regras de escopo do Python (que podem ser irritantes ) também se aplicam f.

Miku
fonte
Eu sei disso, mencionei isso na pergunta. Para CPython 2.6.5, pelo menos. Mas você pode garantir que o mesmo vale para Jython, IronPython e PyPy?
Heikki Toivonen
As regras de escopo do Python também nem sempre são tão claras. Considere isso em CPython 2.6.5: [x for x in [1]]. xestá disponível fora disso. Faça-o em um gerador: (x for x in [1]). Agora xnão está disponível. Parece que me lembro que isso foi alterado no Python 3 para que mesmo com a compreensão da lista xnão vazasse, mas não consigo encontrar a referência agora.
Heikki Toivonen
Eu procurei, mas não encontrei nada significativo por enquanto. Pergunta interessante, no entanto.
Miku de
Na verdade, isso não é uma questão de escopo - a variável f ainda está disponível, mas agora é um identificador para arquivo no estado fechado - o mesmo arquivo que estava aberto anteriormente. A chamada de saída quando o contexto é deixado mudará este estado.
Danny Staple
11

Para responder à pergunta de Heikki nos comentários: sim, esse comportamento de escopo é parte da especificação da linguagem python e funcionará em todo e qualquer Pythons compatível (que inclui PyPy, Jython e IronPython).

Alex Gaynor
fonte