¿Se cierra automáticamente un objeto de archivo cuando su recuento de referencia llega a cero?

Tenía la impresión de que los objetos de archivo se cierran inmediatamente cuando sus recuentos de referencias llegan a 0, de ahí la línea:

foo = open('foo').read() 

obtendría el contenido del archivo y lo cerraría inmediatamente. Sin embargo, después de leer la respuesta a Is close () cuando se usa iterador en un objeto de archivo Python, tengo la impresión de que esto no ocurre, y que llamar a .close() en un objeto de archivo siempre es necesario.

¿La línea de arriba hace lo que creo que está haciendo e incluso si lo hace, es la cosa Pythonic?

La respuesta está en el enlace que ha proporcionado.

El recolector de basura cerrará el archivo cuando destruya el objeto de archivo, pero:

  • Realmente no tienes control sobre cuando sucede.

    Mientras que CPython usa el conteo de referencias para liberar recursos de manera determinista (para que pueda predecir cuándo se destruirá el objeto) otras versiones no tienen que hacerlo. Por ejemplo, tanto Jython como IronPython utilizan JVM y .NET recolector de basura que libera (y finaliza) objetos solo cuando hay necesidad de recuperar memoria y puede que no lo haga para algún objeto hasta el final del progtwig. E incluso para CPython, el algoritmo GC puede cambiar en el futuro ya que el conteo de referencias no es muy eficiente.

  • Si se produce una excepción al cerrar un archivo sobre la destrucción de un objeto, realmente no puede hacer nada al respecto porque no lo sabrá.

Si quieres estar seguro, escribiría el código así:

 from __future__ import with_statement with open('foo') as f: foo = f.read() 

De esa manera, su archivo se cierra como se espera, incluso con excepciones.


Mucho más tarde: aquí hay algo de código con import dis para mostrar cómo el comstackdor trata esto de manera diferente.

 >>> def foo(filename): ... with open(filename) as f: ... return f.read() ... >>> def bar(filename): ... return open(filename).read() ... >>> from dis import dis >>> >>> dis(foo) 2 0 LOAD_GLOBAL 0 (open) 3 LOAD_FAST 0 (filename) 6 CALL_FUNCTION 1 9 DUP_TOP 10 LOAD_ATTR 1 (__exit__) 13 ROT_TWO 14 LOAD_ATTR 2 (__enter__) 17 CALL_FUNCTION 0 20 STORE_FAST 1 (_[1]) 23 SETUP_FINALLY 23 (to 49) 26 LOAD_FAST 1 (_[1]) 29 DELETE_FAST 1 (_[1]) 32 STORE_FAST 2 (f) 3 35 LOAD_FAST 2 (f) 38 LOAD_ATTR 3 (read) 41 CALL_FUNCTION 0 44 RETURN_VALUE 45 POP_BLOCK 46 LOAD_CONST 0 (None) >> 49 WITH_CLEANUP 50 END_FINALLY 51 LOAD_CONST 0 (None) 54 RETURN_VALUE >>> dis(bar) 2 0 LOAD_GLOBAL 0 (open) 3 LOAD_FAST 0 (filename) 6 CALL_FUNCTION 1 9 LOAD_ATTR 1 (read) 12 CALL_FUNCTION 0 15 RETURN_VALUE 

Para la implementación de python en cpython: sí, se garantiza que se cerrará cuando su recuento de referencia llegue a cero.

Para python como un lenguaje abstracto (por ejemplo, incluyendo Jython, IronPython, etc.): no, no se garantiza que esté cerrado. En particular, una implementación de Python puede optar por no usar el recuento de referencias, sino por usar alguna otra forma de GC.

Referencias:

No, Python optimiza la eliminación de los objetos no utilizados, por lo que nunca puede cerrar su archivo (OK al final de su script en la salida se limpiará). @ hughdbrown ha señalado buena solución.