¿Es necesario cerrar archivos que no tienen ninguna referencia a ellos?

Como principiante completo de la progtwigción, estoy tratando de entender los conceptos básicos de abrir y cerrar archivos. Un ejercicio que estoy haciendo es crear un script que me permite copiar el contenido de un archivo a otro.

in_file = open(from_file) indata = in_file.read() out_file = open(to_file, 'w') out_file.write(indata) out_file.close() in_file.close() 

He intentado acortar este código y se me ocurrió esto:

 indata = open(from_file).read() open(to_file, 'w').write(indata) 

Esto funciona y parece un poco más eficiente para mí. Sin embargo, aquí es también donde me confundo. Creo que dejé fuera las referencias a los archivos abiertos; no había necesidad de las variables in_file y out_file. Sin embargo, ¿esto me deja con dos archivos que están abiertos, pero no tienen nada que se refiera a ellos? ¿Cómo cierro estos, o no hay necesidad de hacerlo?

Cualquier ayuda que arroje algo de luz sobre este tema es muy apreciada.

Preguntó sobre los “conceptos básicos”, así que vamos a tomarlo desde la parte superior: cuando abre un archivo, su progtwig obtiene acceso a un recurso del sistema, es decir, a algo fuera del espacio de memoria del progtwig. Esto es básicamente un poco de magia proporcionada por el sistema operativo (una llamada al sistema, en la terminología de Unix). Oculto dentro del objeto de archivo hay una referencia a un “descriptor de archivo”, el recurso de sistema operativo real asociado con el archivo abierto. Al cerrar el archivo se le indica al sistema que libere este recurso.

Como recurso del sistema operativo, la cantidad de archivos que un proceso puede mantener abierto es limitada: hace mucho tiempo, el límite por proceso era de aproximadamente 20 en Unix. En este momento, mi caja de OS X impone un límite de 256 archivos abiertos (aunque este es un límite impuesto, y se puede boost). Otros sistemas pueden establecer límites de unos pocos miles , o en decenas de miles (por usuario, no por proceso en este caso). Cuando su progtwig termina, todos los recursos se liberan automáticamente. Entonces, si su progtwig abre algunos archivos, hace algo con ellos y sale, puede ser descuidado y nunca notará la diferencia. Pero si su progtwig abrirá miles de archivos, hará bien en liberar archivos para evitar exceder los límites del sistema operativo.

Hay otro beneficio al cerrar archivos antes de que finalice el proceso: si abrió un archivo para escribir, si lo cierra, primero se “vaciará el búfer de salida”. Esto significa que las bibliotecas de E / S optimizan el uso del disco mediante la recostackción (“almacenamiento en búfer”) de lo que escribe, y lo guardan en el disco en lotes. Si escribe texto en un archivo e inmediatamente intenta volver a abrirlo y leerlo sin cerrar primero el controlador de salida, verá que no todo está escrito. Además, si su progtwig se cierra demasiado bruscamente (con una señal, u ocasionalmente incluso a través de una salida normal), es posible que la salida nunca se vacíe.

Ya hay muchas otras respuestas sobre cómo lanzar archivos, así que aquí hay una breve lista de los enfoques:

  1. Explícitamente con close() . (Nota para los principiantes de python: ¡No se olviden de los parens! A mis alumnos les gusta escribir in_file.close , que no hace nada).

  2. Recomendado: implícitamente, abriendo archivos con la instrucción with . Se llamará al método close() cuando se llegue al final del bloque with , incluso en el caso de una terminación anormal (de una excepción).

     with open("data.txt") as in_file: data = in_file.read() 
  3. Implícitamente por el administrador de referencia o el recolector de basura, si su motor de Python lo implementa. Esto no se recomienda ya que no es completamente portátil; ver las otras respuestas para más detalles. Es por eso que la statement with fue añadida a python.

  4. Implícitamente, cuando su progtwig termina. Si un archivo está abierto para su salida, esto puede suponer un riesgo de que el progtwig salga antes de que todo se haya vaciado en el disco.

La forma en que Pythonic trata con esto es usar el administrador de contexto :

 with open(from_file) as in_file, open(to_file, 'w') as out_file: indata = in_file.read() out_file.write(indata) 

Utilizado con archivos como este, se asegurará de que se realice toda la limpieza necesaria, incluso si los errores de read() o de write() producen.

El interpeter de python predeterminado, CPython, usa el conteo de referencias. Esto significa que una vez que no hay referencias a un objeto, se recolecta la basura, es decir, se limpia.

En tu caso, haciendo.

 open(to_file, 'w').write(indata) 

creará un objeto de archivo para to_file , pero no lo asignará a un nombre, esto significa que no hay ninguna referencia a él. No es posible manipular el objeto después de esta línea.

CPython detectará esto y limpiará el objeto después de que se haya utilizado. En el caso de un archivo, esto significa cerrarlo automáticamente. En principio, esto está bien, y su progtwig no perderá memoria.

El “problema” es que este mecanismo es un detalle de implementación del intérprete CPython. ¡El estándar de lenguaje no da ninguna garantía explícita ! Si está utilizando un intérprete alternativo como pypy, el cierre automático de los archivos puede demorarse indefinidamente . Esto incluye otras acciones implícitas, como el borrado de escrituras al cerrar.

Este problema también se aplica a otros recursos, por ejemplo, sockets de red. Es una buena práctica manejar siempre explícitamente dichos recursos externos. Desde Python 2.6, la statement hace que este elegante:

 with open(to_file, 'w') as out_file: out_file.write(in_data) 

TLDR: Funciona, pero por favor no lo hagas.

Es una buena práctica usar la palabra clave with cuando se trata de objetos de archivo. Esto tiene la ventaja de que el archivo se cierra correctamente una vez que finaliza su paquete, incluso si se produce una excepción en el camino. También es mucho más corto que escribir bloques try-finally equivalentes:

 >>> with open('workfile', 'r') as f: ... read_data = f.read() >>> f.closed True 

Las respuestas hasta ahora son absolutamente correctas cuando se trabaja en python . Deberías usar el administrador de contexto with open() . Es una gran característica incorporada y ayuda a atajar una tarea de progtwigción común (abrir y cerrar un archivo).

Sin embargo, dado que usted es un principiante y no tendrá acceso a los administradores de contexto y al conteo automatizado de referencias para toda su carrera, abordaré la pregunta desde una postura general de progtwigción .

La primera versión de tu código está perfectamente bien. Abre un archivo, guarda la referencia, lee el archivo y luego lo cierra. Así es como se escribe una gran cantidad de código cuando el idioma no proporciona un acceso directo para la tarea. Lo único que mejoraría es moverse close() hacia donde está abriendo y leyendo el archivo. Una vez que abre y lee el archivo, tiene el contenido en la memoria y ya no necesita que el archivo esté abierto.

 in_file = open(from_file) indata = in_file.read() out_file.close() out_file = open(to_file, 'w') out_file.write(indata) in_file.close() 

Una forma segura de abrir archivos sin tener que preocuparse de no haberlos cerrado es la siguiente:

 with open(from_file, 'r') as in_file: in_data = in_file.read() with open(to_file, 'w') as out_file: outfile.write(in_data)