Como programador de python que todavía anda algo despistado estudiando scala, ahora empiezo a captar la filosófía que hay detrás de este lenguaje de programación. Mientras que python empienza a erradicar poco a poco la programación funcional, en scala su influencia es cada vez mayor hasta el extremo de considerar precindibles la mayoría de los bucles. Aún asi, ambos lenguajes soportan la “compresión de listas” como técnica a medio camino entre funcional y bucle estándar, aunque esta técnica está más orientada a obtener listas a partir de otras listas, y no para realizar cálculos sobre un conjunto de números.

Voy a completar el anterior artículo que trataba del “Estudio función factorial en scala” con algunas formas más “funcionales” de definir el factorial:

La forma más simple de definir la función factorial:

def fact(n:Int) = (1 to n).product

En realidad, 1 to n no es un elemento sintáctico del lenguaje, si no más bien la forma alternativa de escribir la invocación del método 1.to(n). Este método nos genera una secuencia de números desde el 1 al n (equivalente en python a range(1,n+1)).

Curiosamente, también está definido fact(0) gracias a que product da como resultado el elemento neutro 1 en secuencias vacías 1.

Esta forma concisa de calcular el producto es común a todas las secuencias en scala. Faltaría, tan sólo, que operara con BigInts para que fuera perfecta:

def fact(n:Int) = (BigInt(1) to n).product

No es necesario indicar el tipo devuelto por la función puesto que el compilador es capaz de inferirlo de la expresión.

Otra forma funcional sería usando el método reduce, donde se indica explícitamente la operación binaria a realizar entre pares de elementos:

def fact(n:Int) = (BigInt(1) to n).reduce(_*_)

Como operación se pone una especie de plantilla (pattern) que representa la operación binaria de multiplicación. Por comparar, en python se puede hacer algo así:

from operator import __mul__

def fact(n):
   return reduce(__mul__, xrange(1,n+1))

Lamentablemente, el operador funcional 'reduce' está desapareciendo de python por considerarlo complejo de entender en su funcionamiento 2.

Por último, aún existe otra forma funcional de expresar el factorial en scala. Son los “plegados” (folds), similar en funcionamiento a reduce, pero con control sobre la dirección del recorrido y la posibilidad de dar un valor inicial:

def fact(n:Int) = (1 to n).foldLeft(BigInt(1))(_*_)

Seguro que pronto se me ocurrirán más formas.


  1. En el caso del método sum daría el elemento neutro para la suma, o sea, el 0

  2. Personalmente, considero que la desaparición de la programación funcional se debe más a la corta visión de quién sólo ve un encadenamiento de sentencias, en lugar de ver “actores” interaccionando en libre concurrencia. 

 

A raiz de la consulta de un colega, me ha llamado la atención el módulo binascii. Este módulo se encarga de la codificación y decodificación de cadenas binarias para su transmisión en mensajes de texto. Lo habitual es que sea usado por otros módulos como uu, base64 o binhex, por lo que no es nada frecuente ver su uso directo en una aplicación.

Sin embargo, binascii posee algunas funciones que pueden sernos bastante útiles a la hora de simplificar el empaquetado de datos que requieren determinadas APIs, en lugar de usar estructuras más complejas como array o struct. También se revela muy útil a la hora de crear batería de tests donde necesitemos chequear valores binarios.

Estructuras array y struct

Por ejemplo, imaginemos que nuestra API nos pide que empaquetemos un número entero como cuatro bytes. Antes de python3 no existía una forma para controlar el tamaño en bytes de un objeto sin tener que recurrir a alguna estructura especializada. Por ejemplo:

from array import array

def numpack(num):
    a=array('L')
    a.append(num)
    return a.tostring()[::-1]

n=numpack(0xffeeddcc)  # res: \xff\xee\xdd\xcc

En el resultado final ha hecho falta invertir el orden de los bytes debido a que nos estaba usando un orden “little-endian” para su codificación. El orden puede depender del sistema donde estemos trabajando, con lo que no es nada seguro usar este método. Es preferible el uso más especializado de struct donde tendremos algo más de control sobre éste y muchos otros aspectos:

from struct import pack

def numpack(num):
    return pack('!L', num)

n=numpack(0xffeeddcc)  # res: \xff\xee\xdd\xcc

Nota que en la cadena de formato que se pasa a pack() tiene un indicador '!' al principio, con el que indicamos que queremos una ordenación “network (=big-endian)”.

El proceso inverso es tan fácil como usar la función complementaria unpack:

unpack('!L', n)[0]

Codificando mensajes

Lo visto hasta ahora funciona bien cuando tenemos que interaccionar con una API que use tamaños fijos para los datos. Pero, ¿qué pasa cuando los datos son de longitud variable, por ejemplo, un mensaje largo de decenas de bytes? En el mejor de los casos, tendríamos que ir byte a byte, tal vez apoyándonos en array o struct para realizar las conversiones, algo a todas luces resulta bastante tedioso.

Como ya adelanté, el módulo binascii nos va a ayudar en este cometido, en concreto la función b2a_hex y su contraparte a2b_hex.

Por ejemplo, nos envían en un mensaje un entero codificado en multibyte. No sabemos si son 2, 4 u 8 bytes, o incluso podrían ser más bytes de tratarse de un BigInt (entero muy largo). Con binascii podríamos resolverlo así:

from binascii import b2a_hex, a2b_hex

num = int(b2a_hex(msg), 16)

Para el proceso contrario, codificar un entero en una cadena binaria, usaríamos a2b_hex:

h = "%x"%num
if len(h)%2 == 1:
    h = "0" + h

msg = a2b_hex(h)

Hemos tenido cuidado de que la cadena hexadecimal tenga longitud par ya que a2b_hex codifica siempre cada byte a partir de una pareja de dígitos hexadecimales.

Estudio codificaciones unicode

También es posible usar binascii para estudiar las codificaciones de cadenas unicode, lo que hace más sencillo crear tests automáticos para funciones que empleen unicode. Sin muchos adornos, haríamos algo así:

#-*- coding: utf-8 -*-

from binascii import b2a_hex, a2b_hex

cadena = u"Peón \N{BLACK CHESS PAWN}"

print b2a_hex(cadena.encode('utf-8'))

#res: 5065c3b36e20e2999f

Comparando el resultado obtenido con la cadena unicode, vemos que la ó acentuada se codifica en ‘utf-8′ como 0xc3b3, o que la figura de peón negro se codifica como 0xe2999f.

Si cambiamos la codificación por ‘utf-16′ obtenermos como resultado fffe50006500f3006e0020005f26. Además de ser más larga, vemos que se añade al principio fffe, que es lo que se denomina BOM, y que nos indica qué ordenación de bytes se ha usado en la codificación ('FEFF' para Big Endian / 'FFFE' para Little Endian). Con fffe nos indica concretamente que se ha usado la codificación ‘UTF-16 Little Endian’, con lo que tenemos los bytes invertidos para cada caracter codificado (ver más info en el artículo sobre BOM de la wikipedia).

De no desear que se nos añada la marca BOM, podríamos haber forzado la codificación mediante 'utf-16be' ó 'utf-16le' para Big Endian y Little Endian, respectivamente.

 

He estado últimamente trabajando en varios proyectos que me han hecho descuidar un poco el blog. En próximos días espero publicar algunos artículos relacionados que creo serán de interés.

Lo último que estoy haciendo es un desarrollo de aplicación de escritorio con web2py, a pesar de tratarse de un framework para crear aplicaciones web. Corre desde un navegador cualquiera, interaccionando con [jQuery] para la presentación, y con python corriendo por debajo para dar soporte a la capas de datos y lógica de negocio.

¿Por qué web2py?

Como indico en el título, web2py lleva las “pilas incluidas”, que tratándose de python significa que incluye todo lo puedas necesitar. ¡Y no exagero! La selección de librerías que incluye es extensa, permitiendo desarrollar casi cualquier aplicación web que necesites, con herramientas administrativas tanto para el modelo de datos como para editar los ficheros vistas y controladores. Vamos, que con descomprimir el fichero con web2py y un navegador ya tienes todo lo necesario para desarrollar la aplicación, desde una simple aplicación estática, hasta una completa aplicación que funcione en el Google AppEngine. Además posee un sistema de plugins que permite cambiar fácilmente el diseño de la web o añadir funcionalidades.

Hasta ahora, había probado algunos GUIs graficos como pyqt o wxpython que incluyeran algún widget html para renderizar páginas webs y poder controlarlas desde el programa python, algo bastante costoso de programar y con serias incompatibilidades a la hora de presentar la página web. Con web2py, la aplicación de escritorio se orienta a una aplicación web con el servidor web corriendo directamente en el cliente. Web2py permite abstraer algunas facilidades ajax y jquery habituales, aunque no resulta difícil usar el resto de funcionalidades de jquery en las aplicaciones sin tener que ser un experto en programación javascript.

Uno de los problemas que también había tenido hasta ahora era en la distribución de aplicaciones python para windows. Habitualmente, hace falta instalar varias herramientas en el sistema, desde el intérprete python hasta todas las librerías necesarias, algunas complicadas de hacerlas funcionar. Bien es cierto que existen formas de empaquetar una aplicación usando py2exe (py2app en MacOS), pero casi siempre hay alguna cosa que no funciona bien del todo. Web2py viene con todo empaquetado para su uso en windows y macOS, incluyendo el intérprete python y resto de librerías necesarias. Basta descomprimir el fichero y, sin intalación, ejecutar el servidor web y abrir un navegador. Las aplicaciones web son carpetas que podemos copiar de una instalación a otra o, aún más sencillo, usar el interface administrativo para empaquetar e instalar aplicaciones (al estilo tomcat).

Échale un vistazo, y no te dejes las “pilas”.

 

Como continuación al artículo que dediqué al estudio del factorial, voy a explicar cómo se haría este famoso algoritmo usando scala. Tengo que añadir que tan sólo llevo una semana con el lenguaje scala, por lo que es muy probable que haya algún aspecto de este lenguaje que me haya dejado por el camino.

Versión recursiva (y one-line)

La forma básica de la función sería:

def fact(n:Int):BigInt =
    if (n==0) 1
    else n*fact(n-1)

Si se compara con la función recursiva en python, no parece que haya mucha diferencia, con excepción de que en scala existe el tipado de datos.

Esta función es en realidad una sóla línea, por lo que podíamos haberla escrito de esta manera:

def fact(n:Int):BigInt = if (n==0) 1 else n*fact(n-1)

Es una clara señal de la orientación funcional que tiene scala.

Al igual que python, esta función recursiva se corta cuando se sobrepasa un cierto límite de llamadas recursivas para proteger la memoria del sistema.

El compilador de Scala posee una optimización especial denominda de “LLamada Terminal” (Tail Call)1 (optimización que no existe en JVM). Este tipo de optimizaciones son posibles cuando la última línea a ejecutar de la función es únicamente la llamada recursiva a sí misma, con lo cuál hace innecesario guardar el stack de ejecución puesto que no quedarían más líneas para ejecutar.

Para que sea posible aplicar esta optimización de “llamada terminal”, tenemos que reescribir nuestra función de modo que la última línea sea una llamada a sí misma. Para ello usaremos una función acumuladora que se encargue de realizar la multiplicación previamente a la llamada. Casi mejor si vemos el código:

def fact(n:Int):BigInt = {
    def factAcc(n:Int, acc:BigInt):BigInt =
        if (n<=1) acc else factAcc(n-1, n*acc)

    factAcc(n,1)
}

En las últimas versiones de scala existe una “anotación” especial para indicar al compilador de scala que intente aplicar la optimización de “LLamada Terminal”, o que nos dé un aviso de no poder hacerlo. Finalmente, así quedaría el código de nuestra función recursiva:

import scala.annotation.tailrec

def fact(n:Int):BigInt = {
    @tailrec
    def factAcc(n:Int, acc:BigInt):BigInt=
        if (n<=1) acc else factAcc(n-1, n*acc)

    factAcc(n,1)
}

Versión iterativa

Es la versión más sencilla:

def fact(n:Int):BigInt = {
    var res=BigInt(1)
    for (i <- 1 to n)
        res*=i
    res
}

Fórmula de Stirling

Para completar el estudio, podemos ver cómo sería la función de Stiling en Scala, bastante similar, como puede verse, a su versión en python:

import math._

def fact(n:Int):Double =
    sqrt(2*Pi*n)*pow(n/E,n)

  1. Existe algún intento para implementar esta optimización de “Tail Call” en python, con algunos decoradores más o menos funcionales. Si quieres ver motivos en contra, visita el artículo que escribió Guido sobre el tema: http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html 

 

Desconectado de mis tareas habituales depués de algunas semanas viajando por Francia, veo que me quedaron varios proyectos y artículos en dique seco que pretendo recuperar. Con la “reentrada” (o, como dirían los franceses, “rentrée”) me he propuesto algunas metas para esta nueva temporada (por llamarla de algún modo) que ahora empiezo.

Junto a los artículos que tengo previstos, intentaré incorporar al blog más comentarios sobre temas técnicos que me vayan surgiendo en el día a día, preferiblemente relacionados con la programación. Sin llegar a la extensión de un artículo, espero que sirvan como gérmen de desarrollos posteriores más extensos.

Como primeras ideas para esta rentrée, he tomados dos decisiones: centrar mis desarrollos en la máquina virtual java (plataforma JVM) y aprender a programar con scala.

Máquina Virtual Java (JVM)

Hoy en día, la JVM está omnipresente para casi cualquier dispositivo y sistema operativo. Su uso empresarial es muy extendido, tanto para desarrollo en el lado servidor como para clientes móviles. Librerías y paquetes suficientemente robustos y probados completan una gran plataforma donde desarrollar cualquier tipo de aplicativo que podamos pensar.

Al evaluar la robustez de las librerías java, hay que tener en cuenta que java y su JVM están en constante evolución. El paso de Java5 a Java6 sido muy lento debido a las pocas ventajas que ofrecía el cambio frente al coste de tener que adaptar el código; pero con Java7 se incorpora a la máquina virtual el poder trabajar con tipos dinámicos de datos, lo que mejorará bastante el rendimiento de los lenguajes de scripting como jython, jruby ó groovy, por poner algunos ejemplos. Este cambio parece independizar el desarrollo de la JVM del lenguaje java para pasar a ser una plataforma común para la ejecución de aplicaciones, sea cual sea el lenguaje que se haya usado (objetivo similar a lo que tenía que haber sido .Net).

En lo personal, desde hace mucho tiempo que estoy programando en jython, tal como comenté en otro artículo. La llegada de los dispositivos android hace aún más interesante la programación para JVM, así como que las numerosas herramientas de software libre que estoy usando estén para esta plataforma. No quiero decir con ésto que renuncie a utilizar la CPython, la máquina virtual “nativa” que lleva python, siempre que sea necesario. Tan sólo priorizo la plataforma, JVM, frente a las últimas implementaciones del lenguaje python. Espero que el proyecto PyPy facilite un único camino para el desarrollo del lenguaje, independiente de la máquina virtual empleada.

Lenguaje Scala

Poco conozco de este lenguaje, la verdad. En el índice [tiobe] de septiembre de 2011 figura en la posición 50, la última posición que entra en valoración. Pero los comentarios que he leído sobre este lenguaje me han picado tanto la curiosidad que he decidido darle un vistazo. Si quieres un consejo: no te limites a un sólo lenguaje de programación. Sólo comparando con otros lenguajes descubrirás las virtudes y limitaciones de los lenguajes que uses. Sobre todo, intenta aprender algún lenguaje “exótico”, si por exótico se entiende aquél que no se ve en los estudios oficiales de informática. Siempre que te pidan mostrar tus conocimientos de programador, saber programar en un lenguaje “exótico” será visto como que te entusiasma la programación y que tienes capacidad para aprender cosas nuevas por tu cuenta (Python sigue siendo un excelente ejemplo de lenguaje para estas demostraciones).

Algunas características interesantes de Scala:

  • Lenguaje funcional orientado a objeto similar a java, pero superando a éste en simplicidad. Incorpora clausuras y tipado perezoso de datos.
  • Escalable (como indica su nombre: scalable language)
  • Emplea la JVM, aunque también hay versión para .Net. Puede usarse en cualquier sitio que se use java como, por ejemplo, para programación en android.
  • Preparado para la programación concurrente. Sigue el modelo “Actor”, o lo que es lo mismo, todos los objetos son “actores” con su propio entorno de ejecución.

Asociacionismo en torno a python

Tangencialmente, he empezado a meterme en la organización de un evento relacionado con python. Creo importante que todos retomemos los contactos personales e intentar hacer reflotar las ilusiones perdidas por esta crisis que estamos viviendo. Si todo va bien, espero vernos en el Día Python 2011 en Zaragoza dentro de la LSWC’11.

 

Relacionado con el anterior artículo en el que contaba cómo usar mercurial para acceder a repositorios git, la noticia de hoy es que bitbucket soporta también git (además de mercurial). Unido a las herramientas para importar cualquier proyecto de github, SourceForge, Google Code y Codeplex convierten a bitbucket en el lugar ideal para alojar nuestros proyectos, usando cualquier DCVS que elijamos.

Noticia en el blog de bitbucket: http://blog.bitbucket.org/2011/10/03/bitbucket-now-rocks-git/

 

Introducción

Con la llegada de los DCVS (Distributed Concurrent Versions System), se ha convertido en habitual el uso de un sistema de control de versiones en todo desarrollo. La popularización de sitios webs basados en estos sistemas como github, gitorious, bitbucket o googlecode como foros públicos donde compartir código entre programadores hasta el punto de convertirse en auténticas redes sociales, ha hecho de estos sistemas una necesidad para todo desarrollador que se precio, con el consabido dilema de cuál de los sistemas elegir.

Gracias a las extensiones que podemos añadir, cada día es menos transcendente la elección de un DCVS, pudiendo usar cualquier cliente con cualquier otro servidor.

Comparando git y mercurial

Sin entrar en mucho detalle, podemos comparar estos dos sistemas DCVS populares para hacernos una idea:

git

  • Desarrollado en Depende de perl y está pensado para linux (mal soporte en windows)
  • Velocidad: muy rápido
  • Comandos: algo complejo
  • Interface gráfico: no tiene
  • Popularidad muy alta
  • Repositorio público estrella: github

mercurial (hg)

  • Desarrollado en python, con versiones para linux, windows y mac
  • Velocidad: rápido
  • Comandos: sencillo (similar a subversion)
  • Interface gráfico: tortoiseHG para gnome y windows
  • Popularidad alta
  • Repositorio público estrella: bitbucket

Éstos son algunos apuntes rápidos. Evidentemente, hay algunos interfaces gráficos para git y es posible emplear git en windows, pero en mi opinión tiene algunos problemas que necesitan pulirse. Por otro lado, existen varios IDEs como netbeans o eclipse que pueden usar cualquiera de estos DCVS, abstrayendo su uso interno a través de un interface común.

Para un programador de python, la elección debería ser clara: mercurial. Realizado en python y con numerosas extensiones, también desarrolladas en python, parece sin duda la mejor elección. Además, es el sistema de control de versiones más utilizado en proyectos python, incluyendo el desarrollo del lenguaje en si, por lo que se uso es casi obligado si queremos colaborar con otros desarrolladores python.

Pero no hace falta renunciar a nada: desde mercurial también se puede usar repositorios git o subversion. Basta con añadir la extensión adecuada.

En el resto del articulo, me centreré sólo en la extensión hg-git, con la que se posibilita el uso de repositorios git desde mercurial, sin necesidad de instalar ningún cliente de git adicional (no existen dependecias con ningún ejecutable git).

hg-git

Instalación

La última versión de mercurial a la hora de escribir este artículo es la 1.9. Como la versión “estable” de hg-git tiene problemas con esta versión en concreto de mercurial, voy a explicar aquí lo que sería el método manual de instalación, bastante más seguro.

Suponemos que tenemos ya instalado mercurial por lo medios habituales (autoinstalador en windows/instalador de paquetes en linux). Nos será de gran ayuda tener instalado tortoiseHG como interface gráfico para manejar los repositorios. Para windows, la instalación de tortoiseHG incluye todo lo necesario al empotrar un intérprete de python, mercurial y varias extensiones, algunas de ellas necesarias para transformar rutas y nombres de ficheros codificados en MBCS. Los siguientes pasos a ejecutar con mercurial serán más fáciles de aplicar desde la interface de tortoiseHG.

En el emplazamiento que queramos, empezamos por clonar un repositorio con hg-git desde mercurial:

$ hg clone http://bitbucket.org/durin42/hg-git hg-git

Normalmente, yo suelo usar un mismo directorio para agrupas todos los repositorios clonados. Ése podría ser el lugar adecuado para guardar este repositorio.

Añadimos esta extensión a la configuración de mercurial. Normalmente, se hace en el fichero mercurial.ini (windows) o en ~/.hgrc (linux). Si usamos tortoiseHG, desde las "opciones globales" podemos editar directamente este fichero.

Para añadir la extensión:

[extensions]
hggit = <ruta-al-repositorio>\hg-git\hggit

Como anotación, en alguna documentación se recomienda añadir también la extensión opcional bookmarks a la configuración; pero a partir de la versión 1.8 de mercurial, el comando bookmark es parte propia de los comandos de mercurial, por lo tanto este paso ya no es necesario.

Como dependencia, hace falta instalar el módulo de python dulwich para manejo de repositorios git con python. En windows ya viene incluído en tortoiseHG, por lo que no hay que hacer nada más. En linux, viene como paquete instalable (python-dulwich en ubuntu), pero también se podría haber instalado mediante easy_install sin mayor problema. Lo que sí hay que tener cuidado es en asegurarnos que no tenemos instalado el paquete python-git para que no interfiera con el módulo hg-git que estamos configurando.

Como lista final, estas serían las versiones probadas:

  • mercurial (hg) 1.9
  • hg-git 0.2.4
  • dulwich 0.6.1

Utilización

Con hg-git instalado ya podemos acceder, por ejemplo, a los repositorios de github directamente desde mercurial. Basta con especificar que se trata de un repositorio git:

$ hg clone git://github.com/django/django.git django.git

Para realizar un push a github con conexión codificada con SSH:

$ hg push git+ssh://user@github.com/user/myrep.git

Así mismo, si partimos de un repositorio mercurial también podemos “convertirlo” para su uso en git con el siguiente proceso:

$ cd myrep # (dentro del repositorio mercurial)
$ hg bookmark -r default master # marcamos 'default' como 'master'
$ hg push git+ssh://user@github.com/user/myrep.git
$ hg push

Al marcar con el nombre master a default facilitamos la conversión de los datos de mercurial a objetos git. Este proceso sólo es necesario hacerlo la primera vez.

github o bitbucket

En cuanto a elegir entre github o bitbucket, es más una cuestión de gustos. github se ha posicionado como el sistema predilecto para darse a conocer, sobre todo como referencia en los curriculo a la hora de solicitar empleo. En cambio, bitbucket permite el uso de repositorios privados, muy útil para pequeños grupos de trabajo y para colaboraciones en la “nube” (dispositivos móviles).

Ambos son gratuitos, por lo que no debes dejar de probarlos tan sólo por lo que haya podido decir aquí. Es una nueva forma de conocer y darse a conocer entre programadores, algo que sin duda hace de nuestro pequeño mundo algo mucho más grande.

 

Últimamente he necesitado pasar algunos ficheros de una web a codificación utf-8, codificación de caracteres más acorde con lo que se lleva hoy en día. En sistemas linux es una labor que se puede hacer fácilmente con la utilidad iconv:

$ iconv -f cp850 -t utf8 <fichero_entrada.txt >fichero_salida.txt

Pero hay veces que es necesario realizar esta conversión en windows. Si tenemos instalado python, una forma rápida de hacerlo sería:

$ python -c "import sys,codecs;codecs.EncodedFile(sys.stdout,'latin-1','utf-8').writelines(sys.stdin)" <fichero_entrada.txt >fichero_salida.txt

…¡y todo en una sóla línea!1

Tan sólo puntualizar que esta conversión emplea iteradores, por lo que no tiene que ser un problema el tamaño del fichero de texto a convertir.


  1. Para ver más ejemplos de “one-liners” os recomiendo este artículo de Joe di Castro 

 

El grupo Python-Madrid y el Grupo de Usuarios de Linux de la Universidad Carlos III de Madrid, celebrarán el próximo día 15 de Julio de 10:00 a 18:00 el evento Python Madrid 2011. El evento tendrá lugar en la Universidad Carlos III de Madrid, en su campus de Leganés. Se trataran temas como Python, Django, PyGame y mucho más. Toda la información en www.espython.org

 

Si has seguido hasta ahora la serie de artículos sobre descriptores, habrás visto que buena parte de la magia de los objetos en python se debe al método __getattribute__ que todo objeto adquiere de su antecesor común, la clase object.

En el último artículo, donde hablaba de las optimizaciones de los métodos especiales, también comentaba algunas optimizaciones que tenían qué ver con el método __getattribute__ y proponía un ejercicio:

¿Sabrías qué es lo que pasa en el siguiente caso? ¿Se invoca el método getattribute en algún momento? ¿Sería una llamada implícita o explícita?

   obj.__getattribute__("__getattribute__")

Quien se enfrenta a este código por primera vez, lo primero que piensa es que se va a producir una autorecursividad puesto que en el acceso al método __getattribute__ se debería invocar el propio método __getattribute__ y así indefinidamente.

Si embargo, cuando se prueba se ve que funciona tal y como se espera. Entonces, ¿cómo se evita la recursividad?

En el artículo de optimizaciones de los métodos especiales hablábamos de dos optimizaciones (atajos) de las llamadas implícitas a métodos especiales:

  1. Implícitamente, sólo se buscará métodos especiales en la clase, nunca en el diccionario del objeto.

  2. Implícitamente, nunca se accederá a un método especial a través de __getattribute__

La intuición nos dice que aquí está la respuesta de que no tengamos autorecursividad.

Antes de analizar lo que está pasando, señalar que en el acceso a atributos se usa el operador ‘.‘ (punto) que, como cualquier otro operador, está sujeto a las mismas optimizaciones que hemos apuntado. Para su labor, el operador . empleará el método especial __getattribute__.

La invocación obj.__getattribute__("atributo") se produce en dos pasos:

  1. Implícitamente, el operador ‘.‘ accede directamente al método __getattribute__, aplicando las optimizaciones.
  2. Se invoca explícitamente a __getattribute__ para que retorne el valor del "atributo"

Así pues, el resultado final consiste en la combinación de una llamada implícita y otra explícita.

Como corolario, se puede afirmar que “Nunca se invocará a __getattribute__ para acceder a __getattribute__“. No será la primera vez que alguien lo haya intentado.

© 2011 Hyperreals *R Suffusion theme by Sayontan Sinha