{"id":1141,"date":"2012-10-24T17:05:16","date_gmt":"2012-10-24T16:05:16","guid":{"rendered":"http:\/\/www.stenyak.com\/?p=1141"},"modified":"2012-10-24T17:05:16","modified_gmt":"2012-10-24T16:05:16","slug":"liberacion-de-recursos-en-python","status":"publish","type":"post","link":"https:\/\/www.stenyak.com\/?p=1141","title":{"rendered":"Liberaci\u00f3n de recursos en python"},"content":{"rendered":"<div>Pong\u00e1monos en contexto: estamos usando Python, un lenguaje no mucho m\u00e1s viejo que Java, y donde una de las primeras lecciones que se aprenden es que la memoria es gestionada autom\u00e1ticamente.<\/div>\n<div style=\"text-align: left;\">\n<p>Hurrah! Ya no hay que hacer <code>malloc<\/code>s, ni <code>delete<\/code>s, ni tener destructores, ni hostias en vinagre, \u00a0porque python lo hace todo!<\/p>\n<p>&nbsp;<\/p>\n<\/div>\n<div>\u00bfEntonces, por qu\u00e9 nos topamos a menudo con c\u00f3digo como \u00e9ste?:<\/div>\n<blockquote>\n<pre>f = open(\"foo.txt\", \"r\")\ntext = f.readlines()\nf.<strong>close<\/strong>()        # WAT.<\/pre>\n<\/blockquote>\n<div>O bien esto?:<\/div>\n<blockquote>\n<pre><strong>with<\/strong> open(\"foo.txt\", \"r\") as f: # WAT.\n    text = f.readlines()<\/pre>\n<\/blockquote>\n<div>\n<p>Y no hablo solo de ficheros, sino cualquier tipo de recurso: conexiones de red, puertos hardware, primitivas de sincronizaci\u00f3n, memor\u00eda RAM, etc.<\/p>\n<p><span style=\"color: #808080;\"><em>(nota: el texto a continuaci\u00f3n es b\u00e1sicamente un copiapega de un mail \u00a0enviado a una lista de correo privada. lo digo por si algo no encajara&#8230;)<\/em><\/span><\/p>\n<p>Vamos a meternos en detalles (al menos hasta donde yo conozco; y no dudeis en corregirme si veis alg\u00fan fallo, as\u00ed aprendemos todos):<\/p>\n<\/div>\n<h2>No es necesario hacer un close:<\/h2>\n<div>\n<p>Es cierto, no es <strong>estrictamente necesario<\/strong> hacer un <code>close<\/code>, porque por defecto el lenguaje python se encarga de esas tareas mundanas por nosotros.\u00a0El problema es que lo hace <strong>autom\u00e1ticamente<\/strong>, y<strong> a su manera<\/strong>, y puede que <strong>no<\/strong> sea la que nos interese.<\/p>\n<p>De ah\u00ed que se suela hacer un <code>close()<\/code> expl\u00edcito (ya sea con una llamada directa, o mediante el &#8220;<code>with<\/code>&#8221; que he comentado antes).<\/p>\n<p>\u00bfPor qu\u00e9 podr\u00eda no interesarnos lo que hace python por defecto?<br \/>\nPuede ser por muchos motivos:<\/p>\n<\/div>\n<h2>La vida de las variables en python es un detalle de implementaci\u00f3n:<\/h2>\n<div>\n<p>Por ejemplo, el int\u00e9rprete que solemos usar (<a href=\"http:\/\/en.wikipedia.org\/wiki\/CPython\">CPython<\/a>) mantiene los objetos en memoria como m\u00ednimo <strong>hasta que salen de \u00e1mbito<\/strong>, y como m\u00e1ximo <strong>hasta cuando el<\/strong> <em><strong>GC<\/strong><\/em> de CPython <strong>lo determina<\/strong> en alguna de sus pasadas (cuando existen ciclos de referencias, y seg\u00fan el perfil de uso de memoria de nuestro programa).<\/p>\n<p><strong>En cristiano<\/strong>: que una variable que se sale de \u00e1mbito, podr\u00eda eliminarse al momento, o <strong>podr\u00eda tardar media hora<\/strong> en ser eliminada por el GC. Imaginad que las conexiones a una misma c\u00e1mara IP solo se cierran cada diez minutos (porque hemos delegado todo al GC), y que esa c\u00e1mara acepta como m\u00e1ximo 4 conexiones simult\u00e1neas&#8230;<\/p>\n<p>(otras implementaciones como <a href=\"http:\/\/pypy.org\/\">PyPy<\/a>, <a href=\"http:\/\/www.jython.org\/\">Jython<\/a>, <a href=\"http:\/\/ironpython.net\/\">IronPython<\/a>, etc. <em>pueden<\/em> comportarse distinto)<\/p>\n<p>En .NET ocurre lo mismo, como bien dice David (de ah\u00ed viene el jaleo de usar <code>iDisposable<\/code>s incluso para recursos managed&#8230;), y en Java m\u00e1s de lo mismo.<\/p>\n<\/div>\n<h2>Bugs de terceras partes:<\/h2>\n<div>\n<p>Alguna vez hemos sufrido <strong>bugs de conteo de referencias<\/strong>, por alguna librer\u00eda de python escrita en C que no actualizaba correctamente los conteos y provocaba leaks de nuestros propios objetos.<\/p>\n<p>Si esos objetos tenian recursos abiertos, hay que <strong>cerrarlos a mano<\/strong>, o sino seguir\u00e1n abiertos hasta que muera nuestro proceso de python.<\/p>\n<\/div>\n<h2>Bugs propios:<\/h2>\n<div>\n<p>Incluso <strong>asumiendo<\/strong> una gesti\u00f3n de memoria <em>perfecta<\/em> e <em>instant\u00e1nea<\/em> por parte de CPython (que <strong>no<\/strong> es el caso necesariamente, como he explicado), existen casos en que nuestro c\u00f3digo puede estar manteniendo variables en memoria (y con recursos abiertos) sin que nos demos cuenta.<\/p>\n<p>Algunos casos t\u00edpicos:<\/p>\n<\/div>\n<h4>Variables de instancia:<\/h4>\n<blockquote>\n<pre>def __init__(self):\n\u00a0 \u00a0 <strong>self.my_file<\/strong> = open(...)\n\u00a0 \u00a0 print self.my_file.readline()\n\u00a0 \u00a0 # el recurso permanecer\u00e1 abierto al menos\n    # hasta que el objeto <strong>self<\/strong> sea eliminado<\/pre>\n<\/blockquote>\n<h4>Variables dentro de una funci\u00f3n de larga duraci\u00f3n:<\/h4>\n<blockquote>\n<pre><strong>my_file<\/strong> = open(...);\nprint my_file.readline()\nwhile True:\n\u00a0 \u00a0 # bucle principal del programa\n\u00a0 \u00a0 # my_file sigue con el recurso abierto al\n    # menos hasta el <strong>fin de la funci\u00f3n<\/strong><\/pre>\n<\/blockquote>\n<h4>Variables que por su naturaleza son compartidas:<\/h4>\n<div>El ejemplo m\u00e1s claro, un <code>mutex<\/code>. Tambi\u00e9n sem\u00e1foros, bufferes de memoria compartidos por varios hilos, etc.<\/div>\n<h4>Variables referenciadas por closures:<\/h4>\n<div>Aunque salgamos de una funci\u00f3n, los closures alargan la vida de las variables que cogen prestadas de su funci\u00f3n padre:<\/div>\n<blockquote>\n<pre>def my_function():\n\u00a0 \u00a0 <strong>my_file<\/strong> = open(\"foobar\")\n\u00a0 \u00a0 def internal_function():\n\u00a0 \u00a0 \u00a0 \u00a0 return my_file.readline()\n\u00a0 \u00a0 return internal_function\nmy_closure = my_function()\n# <strong>my_file<\/strong> sigue existiendo y con el archivo\n# abierto, hasta que <strong>se destruya my_closure<\/strong><\/pre>\n<\/blockquote>\n<h4>\u00c1mbito de funci\u00f3n, no de bloque:<\/h4>\n<div>Es algo b\u00e1sico de python, pero no est\u00e1 mal repasarlo: en python <strong>no<\/strong> vale <em>definir<\/em>\u00a0una variable <strong>dentro de un bloque<\/strong> (por ej. dentro de un <code>for<\/code>), porque su \u00e1mbito es siempre\u00a0<strong>de funci\u00f3n<\/strong>:<\/div>\n<blockquote>\n<pre>for <strong>path<\/strong> in [\"\/a.txt\", \"b.txt\"]:\n\u00a0 \u00a0 <strong>my_file<\/strong> = fopen(path...)\n\u00a0 \u00a0 print my_file.readline()\nprint <strong>path<\/strong> \u00a0 \u00a0# va a imprimir \"b.txt\", aunque\n              # estemos fuera del bucle.\nprint <strong>my_file<\/strong> # lo mismo pasa con my_file, aunque\n              # pueda ser anti-intuitivo\n<strong>del<\/strong> my_file \u00a0 # si queremos que se elimine esta\n              # referencia a la variable, y el GC\n              # pueda hacer su trabajo en <strong>alg\u00fan<\/strong>\n              # <strong>momento indeterminado<\/strong><\/pre>\n<pre>while True:\n\u00a0 \u00a0 #bucle principal de duraci\u00f3n infinita<\/pre>\n<\/blockquote>\n<div><\/div>\n<div>Bueno, creo que as\u00ed queda una explicaci\u00f3n m\u00e1s completa (y espero que tambi\u00e9n sea t\u00e9cnicamente correcta!) \ud83d\ude42<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Pong\u00e1monos en contexto: estamos usando Python, un lenguaje no mucho m\u00e1s viejo que Java, y donde una de las primeras lecciones que se aprenden es que la memoria es gestionada autom\u00e1ticamente. Hurrah! Ya no hay que hacer mallocs, ni deletes, ni tener destructores, ni hostias en vinagre, \u00a0porque python lo hace todo! &nbsp; \u00bfEntonces, por [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[4],"tags":[13],"_links":{"self":[{"href":"https:\/\/www.stenyak.com\/index.php?rest_route=\/wp\/v2\/posts\/1141"}],"collection":[{"href":"https:\/\/www.stenyak.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.stenyak.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.stenyak.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.stenyak.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1141"}],"version-history":[{"count":0,"href":"https:\/\/www.stenyak.com\/index.php?rest_route=\/wp\/v2\/posts\/1141\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.stenyak.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1141"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.stenyak.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1141"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.stenyak.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1141"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}