Hay exactamente una razón por la cual se prefiere lo siguiente:
with open('filename.txt') as fp:
for line in fp:
print line
Todos estamos mimados por el esquema de conteo de referencias relativamente determinista de CPython para la recolección de basura. Otras implementaciones hipotéticas de Python no cerrarán necesariamente el archivo "lo suficientemente rápido" sin el with
bloque si usan algún otro esquema para recuperar memoria.
En tal implementación, es posible que obtenga un error de "demasiados archivos abiertos" del sistema operativo si su código abre los archivos más rápido de lo que el recolector de basura llama a los finalizadores en los controladores de archivos huérfanos. La solución habitual es activar el GC de inmediato, pero este es un truco desagradable y debe ser realizado por cada función que pueda encontrar el error, incluidas las de las bibliotecas. Qué pesadilla.
O simplemente podrías usar el with
bloque.
Pregunta extra
(Deje de leer ahora si solo le interesan los aspectos objetivos de la pregunta).
¿Por qué no está incluido en el protocolo iterador para objetos de archivo?
Esta es una pregunta subjetiva sobre el diseño de API, por lo que tengo una respuesta subjetiva en dos partes.
A nivel intestinal, esto se siente mal, porque hace que el protocolo iterador haga dos cosas separadas: iterar sobre líneas y cerrar el identificador de archivo, y a menudo es una mala idea hacer que una función de aspecto simple realice dos acciones. En este caso, se siente especialmente mal porque los iteradores se relacionan de una manera cuasifuncional y basada en valores con el contenido de un archivo, pero administrar los identificadores de archivos es una tarea completamente separada. Aplastar ambos, invisiblemente, en una sola acción, es sorprendente para los humanos que leen el código y hace que sea más difícil razonar sobre el comportamiento del programa.
Otros idiomas han llegado esencialmente a la misma conclusión. Haskell coqueteó brevemente con el llamado "IO diferido" que le permite iterar sobre un archivo y cerrarlo automáticamente cuando llega al final de la transmisión, pero en la actualidad se desaconseja usar IO diferido en Haskell, y Haskell los usuarios se han movido principalmente a una administración de recursos más explícita como Conduit, que se comporta más como el with
bloque en Python.
A nivel técnico, hay algunas cosas que puede hacer con un identificador de archivo en Python que no funcionarían tan bien si la iteración cerrara el identificador de archivo. Por ejemplo, supongamos que necesito iterar sobre el archivo dos veces:
with open('filename.txt') as fp:
for line in fp:
...
fp.seek(0)
for line in fp:
...
Si bien este es un caso de uso menos común, considere el hecho de que podría haber agregado las tres líneas de código en la parte inferior a una base de código existente que originalmente tenía las tres líneas superiores. Si la iteración cerrara el archivo, no podría hacerlo. Por lo tanto, mantener la iteración y la administración de recursos separadas hace que sea más fácil componer fragmentos de código en un programa Python más grande y funcional.
La capacidad de composición es una de las características de usabilidad más importantes de un lenguaje o API.