2009-12-02 17 views
31

Yo tenía la impresión de que los objetos de archivo se cierran inmediatamente cuando su recuento de referencia golpean 0, por lo tanto, la línea:¿Se cierra automáticamente un objeto de archivo cuando su recuento de referencias alcanza cero?

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

sería conseguir que el contenido del archivo y cerrar inmediatamente el archivo. Sin embargo, después de leer la respuesta a Is close() necessary when using iterator on a Python file object me da la impresión de que esto no sucede, y de que es necesario llamar al .close() en un objeto de archivo siempre.

¿La línea anterior hace lo que creo que está haciendo e incluso si lo hace, es lo pitónico que hacer?

+1

lo preguntas sobre el objeto fichero de Python o el objeto de archivos subyacente a nivel de sistema operativo? La capa del SO está separada de la capa de Python. ¿Por qué preguntas? –

+1

Agradecería una explicación de lo que ocurre en ambos niveles. Más específicamente, me gustaría saber si escribir esa línea de código podría causar problemas en mi aplicación. Estaba asumiendo cuando Python cierra el objeto de archivo que también se lanza el objeto subyacente. –

Respuesta

26

La respuesta está en el enlace que proporcionó.

recolector de basura cerrar archivo cuando se destruye el objeto de archivo, pero:

  • que realmente no tiene control sobre cuando suceda.

    Mientras que CPython utiliza el recuento de referencias para liberar de forma determinista los recursos (para que pueda predecir cuándo se destruirá el objeto), otras versiones no tienen por qué hacerlo. Por ejemplo, Jython o IronPython usan JVM y .NET recolector de basura que liberan (y finalizan) los objetos solo cuando hay necesidad de recuperar la memoria y pueden no hacer eso para algún objeto hasta el final del programa. E incluso para CPython, el algoritmo GC puede cambiar en el futuro ya que el recuento de referencias no es muy eficiente.

  • si se produce una excepción al cerrar el archivo en la destrucción del objeto de archivo, no se puede hacer nada al respecto porque no lo sabrá.

+1

Entonces, ¿puedo inferir en el sentido general que confiar en los detalles de cómo se implementa la recolección de basura en la versión de Python que estoy usando es una Mala Idea y no Pythonic, y es por eso que la línea de código que tengo en la pregunta es incorrecto? –

+1

@Brent: No es necesariamente incorrecto (si __decide__ depender del comportamiento específico de su implementación). No sé nada de Pythonic ;-). Pero en cualquier entorno administrado, confiar en el recolector de basura (sintonizado en la memoria solo gratuita) para limpiar otros recursos es una dirección equivocada. Por supuesto, si solo se trata de un script corto _y_ simplemente lee el archivo (como en el ejemplo), está bien permitir que GC o incluso el sistema operativo cierren el archivo. –

23

Si usted quiere estar seguro, me gustaría escribir el código como el siguiente:

from __future__ import with_statement 

with open('foo') as f: 
    foo = f.read() 

De esta manera, el archivo se cierra como se esperaba, aunque con excepciones.


Mucho más tarde: Aquí hay un código con import dis para mostrar cómo el compilador trata éstos 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 
+1

Entiendo eso, pero específicamente estoy preguntando si el código hace lo que creo que sí o no. Si no, ¿qué hace, si es así, es el camino correcto? –

+1

Como dicen @Edward Loper y @tomekszpakowicz, lo que ocurra dependerá de la implementación, así que si el "código hace lo que creo que hace o no" dependerá de dónde se está ejecutando. La solución que proporciono es independiente de la implementación. – hughdbrown

+3

+1 para el uso de dis. – dbn

1

No, Python optimizar eliminación de objetos no utilizados por lo que nunca se puede cerrar el archivo (OK al final de la secuencia de comandos en la salida lo hará la limpieza). @ hughdbrown han señalado una buena solución.

9

Para la ejecución del pitón CPython: sí, se garantiza que ser cerrado cuando su recuento de referencias llega 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 utilizar el recuento de referencias, sino utilizar alguna otra forma de GC.

Referencias:

Cuestiones relacionadas