Hurrah! Ya no hay que hacer malloc
s, ni delete
s, ni tener destructores, ni hostias en vinagre, porque python lo hace todo!
f = open("foo.txt", "r") text = f.readlines() f.close() # WAT.
with open("foo.txt", "r") as f: # WAT. text = f.readlines()
Y no hablo solo de ficheros, sino cualquier tipo de recurso: conexiones de red, puertos hardware, primitivas de sincronización, memoría RAM, etc.
(nota: el texto a continuación es básicamente un copiapega de un mail enviado a una lista de correo privada. lo digo por si algo no encajara…)
Vamos a meternos en detalles (al menos hasta donde yo conozco; y no dudeis en corregirme si veis algún fallo, así aprendemos todos):
Es cierto, no es estrictamente necesario hacer un close
, porque por defecto el lenguaje python se encarga de esas tareas mundanas por nosotros. El problema es que lo hace automáticamente, y a su manera, y puede que no sea la que nos interese.
De ahí que se suela hacer un close()
explícito (ya sea con una llamada directa, o mediante el “with
” que he comentado antes).
¿Por qué podría no interesarnos lo que hace python por defecto?
Puede ser por muchos motivos:
Por ejemplo, el intérprete que solemos usar (CPython) mantiene los objetos en memoria como mínimo hasta que salen de ámbito, y como máximo hasta cuando el GC de CPython lo determina en alguna de sus pasadas (cuando existen ciclos de referencias, y según el perfil de uso de memoria de nuestro programa).
En cristiano: que una variable que se sale de ámbito, podría eliminarse al momento, o podría tardar media hora en ser eliminada por el GC. Imaginad que las conexiones a una misma cámara IP solo se cierran cada diez minutos (porque hemos delegado todo al GC), y que esa cámara acepta como máximo 4 conexiones simultáneas…
(otras implementaciones como PyPy, Jython, IronPython, etc. pueden comportarse distinto)
En .NET ocurre lo mismo, como bien dice David (de ahí viene el jaleo de usar iDisposable
s incluso para recursos managed…), y en Java más de lo mismo.
Alguna vez hemos sufrido bugs de conteo de referencias, por alguna librería de python escrita en C que no actualizaba correctamente los conteos y provocaba leaks de nuestros propios objetos.
Si esos objetos tenian recursos abiertos, hay que cerrarlos a mano, o sino seguirán abiertos hasta que muera nuestro proceso de python.
Incluso asumiendo una gestión de memoria perfecta e instantánea por parte de CPython (que no es el caso necesariamente, como he explicado), existen casos en que nuestro código puede estar manteniendo variables en memoria (y con recursos abiertos) sin que nos demos cuenta.
Algunos casos típicos:
def __init__(self): self.my_file = open(...) print self.my_file.readline() # el recurso permanecerá abierto al menos # hasta que el objeto self sea eliminado
my_file = open(...); print my_file.readline() while True: # bucle principal del programa # my_file sigue con el recurso abierto al # menos hasta el fin de la función
mutex
. También semáforos, bufferes de memoria compartidos por varios hilos, etc.def my_function(): my_file = open("foobar") def internal_function(): return my_file.readline() return internal_function my_closure = my_function() # my_file sigue existiendo y con el archivo # abierto, hasta que se destruya my_closure
for
), porque su ámbito es siempre de función:for path in ["/a.txt", "b.txt"]: my_file = fopen(path...) print my_file.readline() print path # va a imprimir "b.txt", aunque # estemos fuera del bucle. print my_file # lo mismo pasa con my_file, aunque # pueda ser anti-intuitivo del my_file # si queremos que se elimine esta # referencia a la variable, y el GC # pueda hacer su trabajo en algún # momento indeterminadowhile True: #bucle principal de duración infinita
Leave a Reply