¿Variable definida con declaración with disponible fuera de bloque con?

90

Considere el siguiente ejemplo:

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

He leído los documentos de idioma (2.7) para with-statement y PEP-343, pero por lo que puedo decir, no dicen nada sobre este asunto.

En CPython 2.6.5 fparece estar definido fuera del bloque with, pero prefiero no confiar en un detalle de implementación que podría cambiar.

Heikki Toivonen
fuente
8
La pregunta de si f estaría disponible o no en el alcance adjunto ya ha sido respondida. Para mí, todo el concepto de gestores de contexto hizo clic cuando me di cuenta de que el concepto de contexto es diferente al de alcance . Aquí hay un enlace a mi sitio web que espero ayude un poco: markus-gattol.name/ws/python.html#context_manager
Tom
1
Exactamente: un contexto es una cuestión de cambiar el estado actual: archivo abierto, archivo cerrado o subproceso bloqueado / desbloqueado. Dispositivo asignado / desasignado. Todas las variables nombradas en el alcance todavía están allí, pero ahora apuntarán a identificadores desasignados / cerrados / desbloqueados.
Danny Staple

Respuestas:

159

Sí, el administrador de contexto estará disponible fuera de la declaración with y eso no depende de la implementación ni de la versión. con declaraciones no crean un nuevo alcance de ejecución.

fuzzyman
fuente
3
Esta es la explicación más clara en mi opinión, por lo que otorga la respuesta aceptada; le dará puntos a Alex y TokenMacGuy por información adicional útil.
Heikki Toivonen
Algo que uno podría olvidar fácilmente si no ha trabajado con Python por un tiempo, la función como sangría, nombre y demás sugiere que no debería poder acceder a él y, sin embargo, puede hacerlo.
Vitaliy Terziev
28

la withsintaxis:

with foo as bar:
    baz()

es aproximadamente azúcar para:

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

Suele resultar útil. Por ejemplo

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

with myLock:
    frob_some_more()

el administrador de contexto puede ser útil más de una vez.

SingleNegationElimination
fuente
Bueno, la reutilización de bloqueos puede o no (no tengo idea, pero sería un error si fueran diferentes), pero las reglas de alcance de Python definitivamente serán las mismas aquí en todas las implementaciones.
fuzzyman
1
nuevamente, esto no es un problema de alcance. El alcance será el mismo. Sin embargo, si la implementación de foo .__ exit__ pone el hilo en un estado detenido, entonces, a menos que el bloqueo tenga una entrada que lo vuelva a bloquear, la segunda declaración no parece que haría nada útil para los bloqueos del hilo.
Danny Staple
16

En caso de que fsea ​​un archivo, aparecerá cerrado fuera del withestado de cuenta.

Por ejemplo, este

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

imprimiría:

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

Puede encontrar los detalles en PEP-0343 en la sección Especificación: La declaración 'con' . Las reglas de alcance de Python (que pueden ser irritantes ) también se aplican f.

miku
fuente
Lo sé, lo mencioné en la pregunta. Para CPython 2.6.5 al menos. Pero, ¿puede garantizar que esto mismo es válido para Jython, IronPython y PyPy?
Heikki Toivonen
Las reglas de alcance de Python tampoco son siempre tan claras. Considere esto en CPython 2.6.5: [x for x in [1]]. xestá disponible fuera de eso. Convertirlo en un generador: (x for x in [1]). Ahora xno está disponible. Creo recordar que esto se cambió en Python 3 para que incluso con la comprensión de la lista xno se filtrara, pero ahora no puedo encontrar la referencia.
Heikki Toivonen
Busqué, pero no encontré nada significativo por ahora. Sin embargo, es una pregunta interesante.
Miku
En realidad, esto no es un asunto de alcance: la variable f todavía está disponible, pero ahora es un identificador para archivar en el estado cerrado, el mismo archivo que estaba abierto anteriormente. La llamada de salida cuando se abandona el contexto cambiará este estado.
Danny Staple
11

Para responder a la pregunta de Heikki en los comentarios: sí, este comportamiento de alcance es parte de la especificación del lenguaje Python y funcionará en todas y cada una de las Pythons compatibles (que incluyen PyPy, Jython e IronPython).

Alex Gaynor
fuente