En Python, si vuelvo dentro de un bloque “con”, ¿se cerrará el archivo?

Considera lo siguiente:

with open(path, mode) as f: return [line for line in f if condition] 

¿Se cerrará el archivo correctamente o el uso de return alguna manera no pasa por el administrador de contexto ?

Sí, actúa como el bloque finally después de un bloque try , es decir, siempre se ejecuta (a menos que el proceso de Python termine de forma inusual, por supuesto).

También se menciona en uno de los ejemplos de PEP-343 que es la especificación para la statement with :

 with locked(myLock): # Code here executes with myLock held. The lock is # guaranteed to be released when the block is left (even # if via return or by an uncaught exception). 

Sin embargo, vale la pena mencionar que no puede detectar fácilmente las excepciones generadas por la llamada open() sin poner el bloque completo dentro de un bloque try..except que generalmente no es lo que uno quiere.

Sí.

 def example(path, mode): with open(path, mode) as f: return [line for line in f if condition] 

..es bastante equivalente a:

 def example(path, mode): f = open(path, mode) try: return [line for line in f if condition] finally: f.close() 

Más exactamente, el método __exit__ en un administrador de contexto siempre se __exit__ al salir del bloque (independientemente de las excepciones, devoluciones, etc.). El método __exit__ del objeto de __exit__ simplemente llama a f.close() (por ejemplo, aquí en CPython )

Sí. De manera más general, el método __exit__ de un With Statement Context Manager se llamará en caso de una return desde dentro del contexto. Esto se puede probar con lo siguiente:

 class MyResource: def __enter__(self): print('Entering context.') return self def __exit__(self, *exc): print('EXITING context.') def fun(): with MyResource(): print('Returning inside with-statement.') return print('Returning outside with-statement.') fun() 

La salida es:

 Entering context. Returning inside with-statement. EXITING context. 

La salida anterior confirma que se llamó a __exit__ pesar de la return anticipada. Como tal, el administrador de contexto no se pasa por alto.

Sí, pero puede haber algún efecto secundario en otros casos, ya que puede hacer algo (como __exit__ búfer) en el bloque __exit__

 import gzip import io def test(data): out = io.BytesIO() with gzip.GzipFile(fileobj=out, mode="wb") as f: f.write(data) return out.getvalue() def test1(data): out = io.BytesIO() with gzip.GzipFile(fileobj=out, mode="wb") as f: f.write(data) return out.getvalue() print(test(b"test"), test1(b"test")) # b'\x1f\x8b\x08\x00\x95\x1b\xb3[\x02\xff' b'\x1f\x8b\x08\x00\x95\x1b\xb3[\x02\xff+I-.\x01\x00\x0c~\x7f\xd8\x04\x00\x00\x00'