Archive for the ‘Django’ Category

¿Va a desaparecer Python?

By aaloy on July 29th, 2010

Cuando en algunas empresas dices que vas a utilizar Python para desarrollar su aplicación normalmente te miran raro. No pocas veces es la primera vez que oyen hablar del lenguaje y te pregunta porqué Python y no PHP, “que es lo que utiliza todo el mundo” o Java si estás en un entorno empresarial.

Por una parte comprendo las reticencias. En un país donde se prima ante todo no correr riesgos, hacer lo que todo el mundo hace, incluso cuando signifique no ser competitivo , está bien visto e innovar está mal visto, decir que vas a hacerlo en Python porqué los costes serán menores, la mantenibilidad será mejor y además su aplicación web irá más rápida que en PHP, es un factor de menos peso que el de hacer lo que hacen los demás.

El otro día esta preocupación llegó a niveles alarmantes cuando se nos dijo que según una investigación interna creían que Python desaparecería en un par de años. Obviamente me sentí en el deber moral de tranquilizar a mi interlocutor y presentarle un pequeño inform de porqué podía estar tranquilo, Python no desaparecería en los próximos años.

Como supongo que esta situación se puede volver a repetir y tras petición popular (bueno, una o dos personas, pero soy fácil de convencer) copio mis conclusiones en este apunte, de modo que pueda servir de referencia a otros programadores y empresas que nos dedicamos a la programación en este excelente lenguaje.

El documento podría ampliarse hasta el infinito, hacer referencia y comparaciones con otros lenguajes y frameworks, pero no es ese su objetivo. Se trata de dar una respuesta argumentada al porqué creo que Python no tiene ninguna posibilidad de desaparecer como lenguaje de programación en los próximos años. De hecho tiene menos posibilidades de desaparecer como lenguaje que otros lenguajes comerciales y cerrados, y si nó que se lo pregunten a la gente de Forté tras su compra por parte de Sun, ahora propiedad de Oracle.

Objetivos de este documento

El objetivo de este documento es el de despejar cualquier duda que pueda existir sobre Python como lenguaje de programación con recorrido y futuro. En concreto se trata de establecer el porqué Python no va a desaparecer y porqué vemos que es una plataforma ideal para el desarrollo de sotware.

¿Qué es Python?

Python es un lenguaje de programación de propósito general, interpretado y orientado a objetos, que tanto puede utilizarse para realizar aplicaciones de línea de comandos, aplicaciones de escritorio como aplicaciones web.

Python fue creado por Guido Van Rossum hace más de veinte años. Actualmente Guido sigue implicado en el desarrollo de Python trabajando para Google, que lo considera un lenguaje estratégico en sus desarrollos. El desarrollo de Python es muy estable, se prima sobre todo la claridad del lenguaje y su legibilidad. Se cuida mucho la compatibilidad hacia atrás, filosofía que se ha transmitido también a frameworks como Django.

El futuro de Python está garantizado por la Python Software Fundation  y se realizan numerosas conferencias anuales en distintos paises. La de este año en Europa tuvo lugar el 22 de julio en Birmingham.

Python es un lenguaje multiplataforma, esto significa que se puede ejecutar sobre servidores Linux, Windows, Sun, etc. Incluso se está ejecutando sobre teléfono Android y Nokia.

Python se utiliza desde la programación de sistemas (Linux es un buen ejemplo de ello), cálculo numérico (http://www.enthought.com/) y la programación web, con una gran variedad de librerías y frameworks. Existen ports para Java, .Net que permiten ejecutar el intérprete Python dentro de estas máquinas virtuales.

Por la gran cantidad de librerías que acompañan al lenguaje, se suele decir que Python viene con las baterías incluidas.

La información general sobre Python, origenes y filosofía del lenguaje se puede encontrar en:

http://www.python.org/doc/faq/es/general/

Casos de éxito:

http://www.python.org/about/success/

De entre ellos destacaría:

Google App Engine http://code.google.com/intl/ca-ES/appengine/ que es el entorno de Cloud Computing de Google. En estos momentos soport Java y Python y se lanzó inicialmente en Python. Soporta el framework Django.

Zope http://zope.org Un sompleto servidor de aplicaciones al estilo de Tomcat.

Plone: http://plone.org Un cms escrito sobre la base de Zope que da soporte a multitud de sitos web, desde los de Ubuntu (Canonical) hasta sitios web de NASA, en ambos casos con millones de visitas.

OpenERP: http://www.openerp.com/ Uno de los ERPs más galardonados. Compitiendo de tu a tu con ERPs comerciales en potencia y características.

BitBucket Es un servicio de hosting que da soporte a multitud de programadores y webs. Está escrito en Python y Django. http://code.djangoproject.com/wiki/DjangoSuccessStoryBitbucket

The Washington Post http://projects.washingtonpost.com/congress/ Periódico de gran tirada con una buena parte de sus desarrollos en Django.

The Washington Times http://www.washingtontimes.com desarrolla sobre un stack opensource en Python y Django y ha publicado varios proyectos abiertos en http://opensource.washingtontimes.com/.

EveryBlock http://www.everyblock.com/ Gestor de noticias integrable en un blog desarrollado en Django.

Facebook Aunque la estrucutra principal de Facebook esté hecha en PHP, toda la parte de gestión de mensajes e informcación en tiempo real está programada en Python. Facebook mantiene varios proyectos Python, entre ellos uno altamente estratégico llamado Tornado, que es el servidor web asíncrono que mueve la programación en tiempo real. Más información en:

http://developers.facebook.com/blog/post/301

http://github.com/facebook/tornado

El módulo wsgi de Tornado se integra muy bien con Django y tanto se puede utilizar el sistema de plantillas de Tornado como el de Django.

Quora http://www.quora.com Ha elegido Python y Django sobre otras alternativas. El fundador de Quora, antiguo empleado de Facebook eligió Python sobre PHP por los problemas que tenía que tratar diariamente trabajando con PHP. La entrevista está en http://www.readwriteweb.com/start/2010/07/picking-the-right-programming-language-for-your-startup.php

Mark Lutz en la última edición de su libro Programming Python cita algunas más: Industrial Light & Magic, Pixar, BitTorrent, U.S. National Weather Service, NASA, NSA, Fermi, Corel, Red Hat, Lockheed Martin, … por citar algunas de las más conocidas.

Un ejercicio que resulta de lo más interesante es hacer una búsqueda de la cadena python en una distribución Linux para darse cuenta del papel que juega Python como lenguaje para el desarrollo de funcionalidades y herramientas.

¿Va a desaparecer Python?

Rotundamente no. Python se utiliza en gran cantidad de empresas que lo consideran un lenguaje estratégico para su evolución. Google esponsoriza el desarrollo del lenguaje y utilidades en su Google Summer of code, por ejemplo http://wiki.python.org/moin/SummerOfCode/2010, pero además forma pare del día a día de empresas como IBM, que le ha dedicado numerosos artículos en el developerworks, ActiveState, Aptana, que recientemente contrató a tiempo completo al desarrollador del plugin para Python de Eclipse, Netbeans (Oracle) que cuenta con una rama de Netbeans orientada a Python, Oracle con documentación sobre cómo integrar Python en el acceso a base de datos. Etc.

Una de las comunidades más activas, sin contar la de la propia Python es la de Django. Actualmente la lista de usuarios de Django, alojada en Google Groups cuenta con más de 17.800 subscriptores, lo que da una idea del alcance del framework y por ende del lenguaje.

Otras comunidades de usuarios se centran también en otros aspectos de Python y en distintas librerías. Para las librerías Qt de Nokia http://qt.nokia.com/ ha lanzado un port para Python de dichas librerías llamado PySide http://www.pyside.org/, lo que da un impulso oficial a Python como lenguaje de desarrollo para Qt además de las librerías pyQt que ya existían de manera extraoficial.

Otra comunidad especialmente activa es la de wxPython y las comunidades de Turbogears, Pylons, pyNumeric, etc. etc.

Un lenguaje de programación es tan potente como las librerías que lo acompañan. Python tiene un buen número de librerías y utilidades que se pueden utilizar justo después de instalarlo (de ahí el batteries included del sobrenombre de Python), pero como se puede ver, hay una gran cantidad de librerías y utilidades que abarcan practicamente todos los ámbitos de la programación.

La opción de PHP

PHP es un lenguaje que nos permite desplegar nuestras aplicaciones en servidores de bajo coste. El éxito del PHP se debe no tanto a la potencia del lenguaje sinó a que los requisitos técnicos y de máquina que se necesitan para poner en marcha una aplicación en PHP los hacen ideales para su explotación masiva por parte de los ISPs.

Cuando se trata de una empresa y con los costes acutales del Hosting, lo que debe primar en el desarrollo de nuevas aplicaciones es la mantenibilidad. Salvo que se utilice un framework com Cake http://cakephp.org/ o Symfony http://www.symfony-project.org/ que nos premite separar la programación en distintas capa, los programas en PHP tienden a ser una amalgama de código de negocio embebido en páginas HTML junto con páginas PHP y librerías.

Esto no quiere decir que no haya muy buenos programas escritos en PHP y muy buenos programadores en PHP, el problema es que es muy difícil separar el grano de la paja y PHP no favorece en nada esta separación. Python utilizado junto al framework Django nos propone una separación clara entre capas e impica tener que pensar qué se está haciendo. Esta reflexión nos lleva normalmente a programas más mantenibles.

Python desde siempre ha primado la mantenibilidad y la claridad del lenguaje y frameworks como Django han hecho suyo este lema.

La elección de un framework PHP nos da la separación en capas y la mantenibilidad que buscamos en los proyectos web que deben perdurar en el tiempo, pero con unos costes importantes: un rendimiento mucho menor, pasando de las más de 300 peticiones por segundo que aguanta un proyecto mínimo de Django a las 34 peticiones por segundo del mismo proyecto desarrollado en uno de estos frameworks PHP.

El otro precio a pagar es la cantidad de líneas de código. El ratio tipico de un programa escrito en PHP contra el mismo programa escrito en Python es de 3 a 5 veces menos líneas de codigo. Más líneas de codigo implican más tiempo (y por tanto normalmente un mayor coste para la empresa) pero sobre todo mucho más código para depurar y mantener. Esto se acentúa incluso más si utilizamos un framework y la programación orientada a objetos en PHP, ya que las construcciones y el código tiende a hacerse muy farragosos.

¿Qué significa la adopción de Python?

Apostar por Python y Django para una empresa que quiere comercializar sus productos on-line es apostar por el futuro y la mantenibilidad. Es una decisión estratégica que puede diferenciarnos de los competidores que estén utilizando otras tecnologías.Incluso cuando los tiempos de desarrollo sean comparables (caso de un desarrollo en PHP sin utilizar un framework y Python+Django) la arquitectura que nos proporciona Django hace que las aplicaciones sean más legibles y mantenibles a largo plazo. La reducción en el número de líneas de código respecto PHP y la legibilidad del lenguage hacen que podamos hacer sin mayores dificultades modificaciones a código que hemos desarrollados meses, incluso años atrás.

Python está preparado para la web: librerías de plantillas, librerías soap, xml, conexión con sistemas de mensajería como RabbitMQ o Beanstalk, librerías Rest como django-piston. Al ser un lenguaje de propósito general no estamos limitados a lo que podemos hacer en un servidor web, podemos utilizar Python en nuestros scripts de sistemas, utilizarlo para generar documentación y estadísticas off-line, las posibilidades sólo están limitadas por nuestra imaginación. Optar por Python significa tener al alcance de la mano librerías y aplicaciones de última generación para los propósitos más dispares: desde cálculo numérico al envío masivo de e-mails, desde la monitorización de sistemas hasta el screen scrapping.

La capacidad que tiene Python para lanzar un depurador integrado o remoto, de desplegar aplicaciones remotamente a través de ssh con aplicaciones como fabric o de crear sencillos servicios web con servidores como web.py o Tornado lo hacen una herramienta de productividad sin competencia en estos momentos. Significa, por tnato un factor competitivo importante: reduce los tempos de desarrollo, pero reduce aún más los tiempos de mantenimiento correctivo y evolutivo.

 

Referencias

Python at Google

http://panela.blog-city.com/python_at_google_greg_stein__sdforum.htm

Curso de Python en Google

http://code.google.com/intl/ca-ES/edu/languages/google-python-class/

Google+Python = world domination?

http://techreport.com/discussions.x/16713

Selling Django to your superiors: Success Stories Panel

http://python.mirocommunity.org/video/1205/selling-django-to-your-superio

Entrevista al fundador de Quora

http://www.readwriteweb.com/start/2010/07/picking-the-right-programming-language-for-your-startup.php

Comparando PHP y Python

http://enbeeone3.com/php-vs-python-analysis

Tutorial de Python en IBM Networks para programadores PHP

http://www.ibm.com/developerworks/opensource/library/os-php-pythonbasics/index.html?ca=dgr-twtrPython4PHPdth-OS

Comparación de Django con diversos frameworks PHP

http://trespams.com/2009/05/10/django-vs-php-framewors/

Switching from PHP to Python

http://www.slideshare.net/aezell/switching-from-php-to-python

Comparando Python y PHP

http://wiki.python.org/moin/PythonVsPhp

The Growth of Dynamic Languages 

http://www.activestate.com/blog/2010/07/growth-dynamic-languages-pythonists-pythonistas-and-pythoneers

Comparación de sintaxis entre php, perl, python y ruby

http://hyperpolyglot.wikidot.com/scripting

TIOBE Programming Community Index for July 2010

http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html 

 

Posted in

Motor de reservas: ¿comprar o crear?

By aaloy on May 15th, 2010

Entendemos como motores de reservas aquella tecnología que nos permite poner a disposición de terceros (vías web, xml, etc) nuestra disponibilidad de habitaciones, permitiendo que se pueda hacer la reserva.

El términio es muy amplio y la solución técnica que le podemos dar no es única. En este artículo trataré de explicar un poco qué alternativas tenemos a la hora de elegir un motor de reservas y los condicionantes que nos pueden llevar a elegir una solución u otra.

A la hora de elegir una solución debemos plantearnos el porqué lo hacemos: si queremos una solución para salir del paso o queremos una solución que nos de flexibilidad. Si necesitamos vender sólo nuestro producto o bien necesitamos, como en el caso de las agencias, vender disponibilidad de camas de un gran número de proveedores.

Veamos las principales alternativas:

Página estática

Listamos las posibilidades en nuestra web y el cliente rellena un formulario. Todo el proceso se realiza manualmente y no hay control de cupo y sí un procesamiento totalmente manual de la reserva.

Pongo esta opción por completitud, pero es la menos aconsejable, pero obviamente es la más rápida de desarrollar y mantener.

El iframe

Un iframe es una página dentro de una página. Es la solución que cuesta menos de poner en producción ya que se limita a unas pocas líneas de código HTML dentro de nuestro portal web. Si no tenemos portal obviamente habrá que crearlo.

Es la solución que presenta más desventajas desde el punto de vista de la hipoteca tecnológica (el precio que pagamos a la larga por elegir una solución), de dependencia del proveedor y por posicionamiento.

Hay que tener en cuenta que es el proveedor del iframe el que tienen el motor de reservas y lo aloja en sus servidores. Nosotros no tenemos control alguno sobre ni sobre el iframe ni nuchas veces sobre los resultados que aparecen.

El grado de integración que podemos tener con el iframe es mínimo, ya que recordémoslo, se trata de una página que no es nuestra y que por tanto tendremos únicamente lo que el proveedor del iframe nos proporcione.

En tema de posiciamiento de nuestra web el iframe representa un gran problema, ya que recordémoslo nuevamente, el iframe no forma parte de nuestra página, y por tanto no vamos a poder posicionar nuestro portal de modo tant eficiente como si todo el contenido fuese nuestro.

En resumen:

  • tiempo de puesta en producción: bajo
  • control: poco
  • dependencia del proveedor: alta
  • personalización: baja
  • indexabilidad: nula
  • desarrollo y mantenimiento: sólo contenido

    
Servicios XML

El motor de reservas sigue estando controlado por un proveedor externo pero este nos proporciona un interfaz xml con el que comunicar con él. Algunos proveedores XML pueden servirnos todo su contenido o bien únicamente contenido con contrato o cupo propio, por lo que también pueden ser una solución para cadenas y pequeños TTOO que no deseen mantener un motor propio.

El desarrollo de la aplicación web es más costoso ya que se necesita hacer una transformación entre los datos que nos suministra el proveedor (texto en XML) y la web y normalmente añadirle reglas de negocio para la presentación de resultados y precios. Se deben aplicar optimizaciones adicionales para agilizar los tiempos de respuesta de nuestra web y añadir una capa adicional de código que nos independice el XML del proveedor de las funcionalidades de nuestra web, de este modo si cambiamos de proveedor XML sólo tendremos que cambiar una pequeña parte de la web.

El grado de integración que podemos tener en nuestro portal web es muy alto, ya que el proveedor se limita a ofrecernos información de disponibilidad y nosotros decidimos qué y como lo presentamos. El posicionamiento por tanto no está limitado por la tecnología XML que utilizamos.

El grado de depenencia con el proveedor es alto, pero tomando algunas precauciones (como la de añadir una capa de procesamiento intermedia) podríamos dado el caso, cambiar de proveedor sin tener que empezar totalmente de cero. También estamos limitados por la interfaz de acceso que nos proporcione el proveedor (la API XML) y por el flujo que tenga del proceso de compra.

En resumen:

  •  tiempo de puesta en producción: medio
  •  control: alto
  •  dependencia del proveedor: media
  •  personalización: alta
  •  indexabilidad: alta
  •  desarrollo y mantenimiento: mapeo del xml y contenidos

Motor propio a medida

Tener un motor propio nos da control total sobre nuestro producto. Podemos elegir qué y cómo vamos a vender y cómo vamos a poner nuestro producto en la web o si vamos a permitir conexiones de terceros.

El motor puede estar optimizado para nuestro tipo de negocio, de modo que se tendría un motor con optimizaciones diferentes para hoteles de costa que para hotels de nieve. Al estar desarrollado a medida podemos alcanzar cotas de rendimiento y adaptación mucho más altas que en cualquiera de los dos casos anteriores.

Mantener un motor propio nos permite desarrollar nuevos productos e ir construyendo nuestra plataforma de venta a medida.

Todo esto tiene un coste, y no es tan solo el precio, tener un motor a medida propio sólo tiene sentido si pensamos invertir en la web al 100% y junto con el área de marketing ir desarrollando nuevas vías de negocio y fidelización de clientes.

Es importante que mantengamos el control del código fuente del motor (del programa), en caso contrario no tendríamos la flexibilidad que estamos buscando y mantendríamos una dependencia alta con el proveedor. Por ejemplo, si nuestro departamento de programación no tiene tiempo para desarollarlo de manera interna, podemos externalizar el desarrollo y asegurarnos de que el proveedor transfiere el código fuente y el conocimiento de cómo está hecho a nuestros desarrolladores.

El motor será todo lo complicado o sencillo que nuestro negocio requiera. Puede ser una simple tabla con las camas o un completo sistema de disponibilidad y ofertas.

En resumen:

  • tiempo de puesta en producción: medio o alto
  • control: muy alto
  • dependencia del proveedor: baja (si hay transferencia de código y conocimiento)
  • personalización: alta
  • indexabilidad: alta
  • desarrollo y mantenimiento: desarrollo  y contenidos
Independientemente de la solución que elijamos, tenemos que verificar que podemos acceder a las estadísticas de reservas, que podemos exportar los datos que introducimos y ver si hay alguna posibilidad de que el sistema elegido se pueda comunicar con nuestro backoffice de gestión principal.

Éste es uno de los problemas principales: la venta web no se comunica con el backoffice de gestión y eso hace que haya un trabajo manual y rutinario que se podría evitar. A la hora de cambiar el backoffice convendría pedir siempre al vendedor sobre las posibilidades de importación de datos de venta y facturación del mismo. Un backoffice que permita la importación de datos en un formato estándard (texto plano, csv o xml por ejemplo) nos permitirá en un futuro integrar mejor las reservas que vengan de la web sin que tengamos que tener un programa que lo haga todo, pero eso es un tema que dejaré para otro día.

Cada una de estas opciones representa una apuesta mayor en la comercialización web y una implicación mayor de nuestros departamentos de IT (o de los partners que tengamos) y de los departamentos de marketing y ventas. Cuanto mayor es la apuesta mayor es el riesgo, pero también son mayores los beneficios.

Desde la blogosfera actualmente hay un movimiento importante contra el recorte ministerial en I+D, la frase que apunta Ricardo Galli también nos puede servir como conclusión en este apunte:

Desde la revolución agraria las sociedades más desarrolladas no fueron las que podían comprar las herramientas, sino las que tenían los conocimientos y recursos para construirlas. Por ello en los países más desarrollados aproximadamente el 80% de la financiación de I+D proviene de fondos públicos. EEUU invierte en I+D  casi el triple del PIB que España, la media europea es el doble, y algunos paises europeos casi cuatro veces más. Las tijeras no generan atajos.”

Para comprar sólo necesitas dinero, pero te quedas sin el conocimiento.

Desde el punto de vista de control de negocio y mínima hipoteca tecnológica mis soluciones preferidas son el XML y el motor propio, pero obviamente cada uno conocerá mejor que nadie su situación. Espero que este apunte pueda servir para valorar las alternativas y encontrar la que mejor se adapte a las necesidades de cada negocio.

Nota: Este artículo fué publicado inicialmente en la Comunidad HostelTur.
Posted in

Creant bits: el déjà vu

By aaloy on January 9th, 2010

El día 29 de enero en la sala de formación del Parc Bit tendrá lugar una nueva edición del creant bits. Esta presentación está destinada a todos aquellos que no puedieron asistir a la primera charla.

Más información sobre el evento en Trespams.

Posted in

Feliz 2010!

By aaloy on December 28th, 2009
Posted in

Creant bits amb Python i Django

By aaloy on December 7th, 2009

El pasado viernes 4 de diciembre, entre las 4 i más de las 9 de la noche tuvo lugar la reunió “fent bits”, dedicada a Python y Django. Nos reunimos más de 25 programadores interesados en Python y Django, en la tecnología y en lo que los lenguajes dinámicos nos pueden ofrecer.

El workshow surgió de un twitt, a partir de ahí generé un apunte en mi blog personal y en poco tiempo había más participantes que disponibilidad de sitio.

La experiencia ha sido fantástica, tanto que por poco que podamos vamos a repetir.

Algunos enlaces del evento:

Se pueden obtener el código de ejemplo a través del repositorio appfusedjango de Google Code y hemos creado un site para la documentación de los ejemplos realizada en Sphinx.

Como incubados en el Parc Bit, el Incubit permite a APSL utilizar (previa reserva) las salas de formación y conferencias. Gracias a esto este evento ha sido posible, ya que el Parc Bit nos ha cedido tanto la sala como el proyector y la valiosa colaboración de Guillem que nos ayudó a con las conexiones de la sala.

Posted in

Avance de diseño

By aaloy on October 22nd, 2009

Para diferenciarlo del blog la nueva imagen de la web potencia el color blanco y aprovecha el logo para dar el toque de color.

La página principal queremos que sea límpia y que sirva de vía de contacto para mantener a nuestros clientes y amigos al corriente de nuestras actividades.

Posted in
Tags

Una prueba de concepto: APSLHOTELES

By aaloy on October 11th, 2009

Ayer por la noche abrimos al público nuestra prueba de concepto de lo que puede ser una página de reservas de hoteles: hotelestest. Al hablar de prueba de concepto me refiero a poder mostrar lo que se puede hacer con tecnología Python y Django en cuanto a aplicaciones orientadas al sector turístico.

En nuestra comunidad (Balears) un tanto por ciento muy elevado de la informática gira en torno al negocio turístico (bueno, un tanto por ciento muy elevado de la economía para ser exactos). Cuando como empresa vas a hablar con alguien del sector y le dices que le harás su aplicación en Python y Django te encuentras normalmente con caras extrañas, salvo honrosas excepciones no conocen de qué les estás hablando. Han oido hablar de PHP, Java o punto-net y no ven claro lo que se puede hacer.

Nuestra prueba de concepto tiene por objeto mostrar que desarrollar una aplicacion en Python y Django es sencillo, que la aplicación va tan rápida como la que más. Para hacer la demostración necesitábamos que la página se conectase a un motor XML/SOAP de un tercero, ya que de este modo se cubre el uso más habitual de este tipo de aplicaciones: una aplicación web donde el motor y la capa de presentación están separadas por un servicio XML.

De los proveedores XML que contactamos recibimos una muy buena acogida por parte de Valadis/Versys. Nos dieron muchas facilidades para poder utilizar su entorno XML de pruebas. Su XML es muy sencillo de mapear y es bastante rápido, esto junto con su buena disposición nos decidió a utilizarlo como base de nuestra web, des de aquí muchas gracias por vuestra colaboración gente.

La ideas que se presentan se pueden desarrollar con otros proveedores siempre que cumplan unos mínimos. La web estará siempre condicionada por las posibilidades del XML y por la velocidad del mismo.

Así pues, tenemos una aplicación B2C que permite reservar hoteles y que conecta al motor XML de test de Valadis/Versys como proveedor externo. Como buen entorno de demo y pruebas he de decir que está en continua evolución y que los datos que se muestran son solo un subconjunto limitado de las que se tendrían en un entrono de producción. No nos fijemos pues en los datos, ni en si una foto o descripción sale o no sale, sinó en la parte importante de lo que se intenta mostrar: la tecnología subyacente.

Por cierto, la web se puede probar sin problemas, no se realiza pago alguno ni control de la tarjeta de credito. Obviamente tampoco se envía la reserva al hotel.

Una vez hecha la introducción vayamos a diseccionar a la bestia:

El desarrollo

Tal como he comentado la aplicación está desarrollada utilizando Python y Django, para el control de versiones se ha utilizado Subversion y Trac para el control del proyecto y la documentación.

Para crear la aplicación hemos mapeado el XML a objetos Python con la librería lxml, una librería que envuelve la libreías C libxml2 y libxslt. La velocidad del C y la expresividad de Python en una combinación de alto rendimento.

Para la depuración y programación hemos utilizado django-extensions, debug_toolbar, ipdb i ipython. La primer librería nos proporciona un conjunto de utilidades para la administración de la aplicaicón; debug_toolbar nos permite controlar en todo momento qué plantilla se utiliza, sus herencias, el sql que se genera, etc. La aplicación ipdb es un depurador de línea de ocmando, como el pdb pero con autocomepletado y resaltado de sintaxis; ipython es una consola Python que Django aprovecha si está instalada proporcioando autocompletado, resaltado de sintaxis y un gran número de comandos extra.

Los editores más habituales en el desarrollo han sido Netbeans, Eclipse, Vim, Kate i Notepad++ (en Windows). El editor importa poco, todos están configurados para trabajar en UTF-8 y utilizar tabuladores de 4 espacios. Con esto tenemos suficientes para poder utilizar el editor más conveniente en cada momento.

Para las páginas de contenido estático utilizamos django-page-cms. La idea es mostrar cómo un gestor de contenidos se puede integrar en la aplicación.  Los css y javascript se comprimen antes de servirse gracias a django-compress. Esto quiere decir que podemos cachear los archivos meses, simplemente cuando cambia el css o el javascript generamos un nuevo archivo con un nombre distinto automáticamente.

La configuración de sistemas.

El entorno de ejecución de independiza de la máquina con un chroot y además hemos creado un entorno virtual separado para la aplicación utilizando virtualenv, esto os permite tener un control total sobre la librerías que instalamos y el Python Path.

Siguiendo la mejores prácticas recomendadas hemos eparado los dominios que sirven el contenido estático del contenido dinámico. Esto es realmente sencillo en Django, sólo tienes que acordarte de escribir {{MEDIA_URL}} en los enlaces a imágenes, javascript y css.

Dado que la aplicación se ejecuta en una máquina que comparte sus recursos con varias aplicaciones más, queríamos que el consumo de memoria y recursos fuese mínimo. Para ello optamos por utilizar el serviodr nginx en lugar de Apache y lo configuramos para que sirviera tanto el contenido estático como para actuar de proxy inverso hacia el motor WSGI que ejecuta la aplicación Django.

Para el motor utilizamos el WSGI de CherrPy, una pila http de alto rendimiento escrita en Python (en breve probaremos también de utilizar Tornado). Con ello mantenemos una relación de consumo de recursos de máquina y rendimiento bastante óptima y una escalabilidad horizontal realmente fabulosa. Con un simple balanceador podemos ir añadiendo entornos chroot con un simple “copiar y pegar” y dentro de cada chroot podemos tener tantas instancias de la aplicación como  soporte la máquina.

Seguramente no sería necesario complicarse tanto la vida para una prueba de concepto, pero la idea es mostrar lo que se puede hacer técnicamente tanto en programación como en sistemas, y cómo el framework y los recursos se adaptan a las necesidades actuales de rendimiento. En pocas palabras, que podemos escalar hacia arriba y hacia abajo.

01
vendor_id   : GenuineIntel
02
cpu family  : 6
03
model       : 15
04
model name  : Intel(R) Pentium(R) Dual  CPU  E2180  @ 2.00GHz
05
stepping    : 13
06
cpu MHz     : 1994.999
07
cache size  : 1024 KB
08
 
09
top - 08:52:55 up 729 days, 13:56,  0 users,  load average: 0.07, 0.02, 0.00
10
Tasks: 126 total,   1 running, 125 sleeping,   0 stopped,   0 zombie
11
Cpu(s):  0.0%us,  0.2%sy,  0.0%ni, 99.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
12
Mem:   1015232k total,   984036k used,    31196k free,   154472k buffers
13
Swap:  2097144k total,     2948k used,  2094196k free,    90536k cached

 

La aplicación

En la página principal nos encontramos con el buscador, el buscador típico añadiría. Utilizamos jquery-ui para el control de calendario y mostramos las funciones de autocompletado en los apartados destinos y nombre de hotel. Los errores se muestran en pantalla de manera poco intrusiva, sin mover la página. Para ello utilizamos plugins de jQuery y las capacidades de serialización de Python y Django complementándolo con la validación de formularios de Django. Es mucho más sencillo de lo que parece.

Se puede ver también la utilización que se hace del CMS en las páginas de Aviso Legal por ejemplo y podemos ver cómo se muestran dos grandes grupos de ofertas. Dichas ofertas se crean en la parte de administración y no son para nada sofisticadas, pero sirven para postrar el dinamismo que se puede conseguir uniendo backoffice y presentación y el concepto de url semántica.

Si hacemos clic sobre una oferta o directamente desde el buscador iremos a la página de resultados. Recordemos una vez más que es un entorno de pruebas y que los datos son los que son. La idea es mostrar cómo los datos del hotel se cargan dinámicamente mediante una llamada Ajax y cómo la aplicación mantiene los datos de selección.

En esta página hemos puesto también un filtro javascript. Pulsando sobre el link de filtro por categoría desaparecen los hoteles de esa categoría. Se puede utilizar la misma idea para filtrar el resultado por otros campos (rango de precios, tipo de habitación, etc).

A partir de aquí ya vamos a las pantallas que obtendrían al información del cliente y completarían la reserva, así como la pantalla de agradecimiento. Nada destacable técnicamente que no se hay visto ya en pantallas anteriores.

Paso a producción

Esta aplicación no va a pasar a producción. Recordemos que es una prueba de concepto, sirve para mostrar ideas de programación, conceptos tecnológicos, de optimización, diseño y maquetación. Lo que sí sería factible sería desarrollar una aplicación a medida utilizando estas tecnologías. La comercialización será siempre de un tercero, APSL se limita a la parte técnica. En todo caso sería necesario desarrollar una serie de módulos que en la prueba de concepto se han obviado:

  • Definir los texos de los contenidos estáticos.
  • Creación de filtros javascript
  • Paginación de resultados
  • Conexión a una pasarela de pago
  • Generación y envío del bono al cliente
  • Backoffice de gestión y control
  • Optimizaciones adiconales como proxy-cache de imágenes y balanceo de carga.
  • Política de backups.

La aplicacción tal como está y en sus posibles evoluciones tiene por objeto que gente como APSL que nos dedicamos al desarrollo de soluciones web con Python y Django puedan mostrar al mundo turístico lo que se puede hacer. Consideramos la aplicación como un proyecto mascota: dedicamos las horas que queremos y podemos. Si logramos acercar estos conceptos tecnológicos al sector turístico nuestro trabajo habrá valido la pena.

Trucos del administrador de Django

By aaloy on September 1st, 2009

Aunque en la documentación del administrador lo dice, a menudo estmos tan acostumbrados a hacer trabajo con campos que nos olvidamos que al `list_display` del admin de Django podemos usar cualquier atributo o método susceptible de ser llamado (entre otras opciones).

Esto abre todo un mundo de posibilidades a la hora de presentar información en forma tabular dentro del admin, podemos crear enlaces hacia otras secciones, mostrar imágenes, todo nos pueda ocurre y que preferiblemente tenga algún tipo de vínculo con el modelo.

Un ejemplo muy sencillo, supongamos que tenemos un modelo que tiene una imagen asociada, queremos que en el listado nos aparezca esta imagen. Para ello haremos:

def get_foto(self):
    if self.foto:
        return '' % self.foto.url
    else:
        return ""
get_foto.allow_tags = True
get_foto.short_description = 'Foto'

y en `admin.py` añadiremos a la configuración del administrador de nuestro modelo

    list_display = ('get_foto', ) #añade aquí los otros campos que quieras

Podemos complicarlo tanto cómo queramos, un ejemplo lo tenemos al Django Snippet de [Admin list Thumbnail](http://www.djangosnippets.org/snippets/239/ “Admin List Thumbnail”), que complica la función para crear las miniaturas de las imágenes que se presentarán y disminuir así el peso de la página.

De este ejemplo es importante notar el el uso que se hace de `allow_tags`, si el método tiene esta propiedad Django no escapará el html que le pasemos, dándonos la oportunidad de presentar todo tipo de código html, bajo nuestra responsabilidad, claro.

Posted in

Decoradores en Python

By aaloy on August 31st, 2009

¿Qué es un decorador?

Un decorador es el nombre de un patrón de diseño. Los decoradores alteran de manera dinámica la funcionalidad de una función, método o clase sin tener que hacer subclases o cambiar el código fuente de la clase decorada. En el sentido de Python un decorador es algo más, incluye el patrón de diseño, pero va más allá, Bruce Eckel los asimila a las macros de Lisp.

Los decoradores y su utilización en nuestros programas nos ayudan a hacer nuestro código más limpio, a autodocumentarlo y, a diferencia otros lenguajes, no requieren que nos aprendamos otro lenguaje de programación distingo (cómo pasa con las anotaciones de Java por ejemplo). En su utilización podemos simular la programación orientada a aspectos (AOP) o utilizarlos para añadir sistemas de control a nuestras funciones, de log, caché, … Las posibilidades son infinitas.

Los decoradores forman parte de Python desde la versión 2.4 y cómo dice Michele Simionato nos aportan lo siguiente:

  • Reducen el código común y repetitivo (el llamado código boilerplate).
  • Favorecen la separación de responsabilidades del código
  • Aumentan la legibilidad y la mantenibilidad
  • Los decoradores son explícitos.

Clasificación de los decoradores

Podemos dividir los decoradores en grupos:

  • Según los parámetros que admiten:
    • No admiten parámetros
    • Sí admiten parámetros
  •  Según si preservan la firma o signatura del método al que decoran:
    • Decoradores no que preservan la firma
    • Decoradores que sí la preservan

Los decoradores más sencillos son aquellos que no admiten parámetros y no preservan la firma

Un decorador que no hace nada

Para empezar crearemos un decorador que el que hará se convertir cualquier función en un /dev/null, es decir, no devolverá nada y no hará nada con la función. Llamaremos a nuestro decorador forat_negre, agujero negro.

1
def forat_negre(f):     
2
    def none():
3
         pass     
4
    return none 
5
@forat_negre 
6
 
7
def di_hola():
8
     return "hola"

Si ejecutamos di_hola() no tendremos resultado alguno, mejor dicho, tendremos `None` como resultado de la función que estamos decorando.
La sintaxis @ del decorador de Python es lo que se denomina syntactic sugar, es decir, una manera de escribir las cosas que aumenta la legibilitat del código. Sin embargo, debemos tener presente que el decorador se podría haber escrito como:

1
di_hola = forat_negre(di_hola)
2
    di_hola()

y tendríamos el mismo decorador. Recordemos que las funciones son objetos de primera clase en PYthon y que se pueden asignar y pasar como parámetros.

Aunque el ejemplo es muy sencillo nos sirve para ver lo siguiente:
Un decorador no es más que un envoltorio de una función y por lo tanto tiene que devolver una función, más concretamente un _callable_, para entendernos, cualquier cosa que poniendo un doble paréntesis al lado () no pete.

01
def retorna_objecte(f):
02
   ....:     def obj():
03
   ....:         return object()
04
   ....:     return obj
05
   ....:
06
 
07
In [17]: def di_hola():
08
   ....:     return "Hola"
09
   ....:
10
 
11
In [18]: di_hola = retorna_objecte(di_hola)
12
 
13
In [19]: di_hola()
14
Out[19]: <object object at 0xf7f745e8>
15

A nuestro decorardor `forat_negre` le hemos pasado una función sin parámetros. Si intentamos pasarle una función con parámetros nos encontraremos con una pequeña sorpresa…

1
@forat_negre
2
def suma(a,b):
3
return a,b
4
 
5
suma(2,3)
6
 
7
TypeError Traceback (most recent call last)
8
TypeError: none() takes no arguments (2 given)
9

que por otro lado es del todo normal, hemos definido el `agujero_negro` de tal manera que devuelve una función sin parámetros, así que si le intentamos pasar los parámetros que tenía la función decorada sencillamente se queja y peta.

Vamos a definir un poco mejor nuestro decorador para que esto no nos pase y que pueda admitir al menos tantos parámetros como la función que decora.

01
def forat_negre(f):
02
    "d'aquí no surt res"
03
    def none(*args, **kw_args):
04
        pass
05
    return none
06
 
07
@forat_negre
08
def suma(a,b):
09
    "suma dos parametres qualsevols si pot"
10
    return a+b
11
 
12
suma(2,2)
13

Ahora ya no da error. Así pues tenemos *otra conclusión*: además de devolver una función, tenemos que procurar que la definición de las función que devolvemos admita al menos el mismo número de parámetros que la función que queremos decorar. Si no sabemos cuántos son estos nos curamos en salud con *args* y *kw_args*.

Fijémonos que no hemos mantenido la firma de la función. Si como experimento intentáis hacer un `help(suma)` no obtenemos la ayuda de la función original. Volveremos sobre esto un poco más adelante. Por ahora ya sabemos como crear decoradores simples a partir de una función.

Haciendo decoradores no intrusivos

Si habéis hecho un `help(suma)` o un `suma.__name__` quizás alguno se habrá sorprendido al ver que el nombre de la función es `_none_` en lugar de la esperada `suma`. Si repasáis lo que hemos hecho tampoco es de extrañar: hemos sustituido la función original por otra. Recordamos que el decorador f aplicado sobre la función g es equivalente a hacer g = f(g).

Lo aconsejable sería que el decorador fuera capaz de mantener la documentación y el nombre de la función que decora, ya que de este modo se simplificaría el uso de la función y los autocompletadores de código no se vuelverían tan locos.

Esto lo podemos hacer de dos maneras: la larga y la corta

La manera larga

1
def forat_negre(f):
2
    def none(*args, **kw_args):
3
        pass
4
    none.__doc__= f.__doc__
5
    none.__dict__= f.__dict__
6
    none.__name__= f.__name__
7
    return none
8

Con las tres instrucciones adicionales que hemos puesto volvemos a recuperar los metadatos de la función original que pasamos al decorador. Si ahora hacemos un `help` veremos que èste devuelve el nombre de la función correcta __suma__ y que la ayuda también es la suya.

1
Help on function suma in module __main__:
2
 
3
suma(*args, **kw_args)
4
    Suma dos parametres qualsevols si pot
5

La manera corta

Preservar los metadatos es bastante útil y común, de hecho en el módulo `functools` encontramos la función `wraps` que es en sí misma un decorador y que hace precisamente ésto. De este modo el código anterior quedaría:

1
from functools import wraps
2
 
3
def forat_negre(f):
4
    @wraps(f)
5
    def none(*args, **kw_args):
6
        pass
7
    return none
8

Observemos que hemos utilizado un decorador para crear otro decorador. Veremos más utilización de decoradores un poco más adelante.

Un decorador con argumentos

El decorador que hemos programado en el apartado anterior era bastante simple, hacía muy poca cosa y no tenía parámetros. Si queremos crear decoradores tenemos que hacer primero de todo que sean útiles, y también nos encontraremos con la necesidad de que estos decoradores admitan parámetros.

En Django, por ejemplo, podéis encontrar que el decorador de cache admite parámetros que nos permiten decirle durante cuánto tiempo tiene que cachear los resultados, o el decorador `vary_on_headers`, que nos permite modificar el contenido de la respuesta de las vistas añadiendo las cabeceras que indicamos.

Vamos a ver como lo podemos conseguir nosotros. También hay dos maneras de hacerlo, la clara y la compleja. La manera clara es la que recomendamos y utiliza una clase para hacer el decorador, la compleja requiere más esfuerza para entender qué está haciendo el decorador, es más corta, pero personalmente prefiero un código más legible.

Los decoradores que hemos programado como funciones se pueden crear también como clases, pero en este caso, creo que la definición en forma de funciones es más sencilla de seguir, y nos permitirá distinguir claramente entre los dos tipos de decoradores: los que no admiten parámetros que se construyen preferentemente mediante funciones y los que admiten parámetros, que se construyen preferentemente usando clases.

Para seguir con el agujero negro, ahora en nuestro ejemplo haremos que se muestre el resultado de la funcion que decoramos o no de manera aleatoria. Pora ello pasaremos al decorador una función como parámetro que al ser ejecutada determinará si se tiene que mostrar el resultado de la función decorada o no

El método claro de hacer decoradores con argumentos

01
#!/usr/bin/env python
02
# -*- coding: UTF-8 -*-
03
import random
04
 
05
class forat_negre_sonat(object):
06
    "Un decorador amb fam"
07
    def __init__(self, mostrar):
08
        self.mostrar = mostrar
09
 
10
    def __call__(self, f):
11
        def none(*args, **kw_args):
12
            if self.mostrar():
13
                return f(*args, **kw_args)
14
            else:
15
                return "Nop"
16
        return none
17
 
18
@forat_negre_sonat(mostrar = lambda :random.choice((True, False)))
19
def suma(a, b):
20
    "Suma dos elements que li passam com a paràmetre"
21
    return a+b
22
 
23
if __name__=="__main__":
24
    print suma(2,3)
25
    print suma(5,6)
26
    print suma(9,5)
27

Observemos el código:

1. Hemos creado una clase Python que a su constructor (el `__init__`) puede tomar el parámetro o parámetros que queramos. Es un constructor normal, así que admite parámetros por defecto.

2. Recordemos que hemos dicho que el decorador tiene que ser un objeto llamable (callable), en una clase, la _llamabilidad_ la da el método `__call__`. Esta clase la definiremos de forma que obtenga la función a decorar com un parámetro. De este modo tenemos acceso tanto a los parámetros del decorador, que hemos pasado al constructor, como a la función decorada, que hemos pasado como parámetro al call.

Después de esto ya sólo queda encapsular la llamada como lo hacíamos al caso anterior, devolviendo el decorador en lugar de la función a decorar.

En el ejemplo además, he intentado mostrar que el parámetro puede ser el que nosotros queramos, en concreto he pasado una función anónima, creada con `lambda` que es la que se encarga de establecer la aleatoriedad del resultado.

Si queréis podemos hacer este decorador un poco más completo, haciendo que admita además de funciones valores y que preserve el nombre y documentación de la función decorada.

01
#!/usr/bin/env python
02
# -*- coding: UTF-8 -*-
03
import random
04
 
05
class forat_negre_sonat(object):
06
    "Un decorador amb fam"
07
    def __init__(self, mostrar=None):
08
        self.mostrar = mostrar
09
 
10
    def __call__(self, f):
11
        def none(*args, **kw_args):
12
            if callable(self.mostrar):
13
                opcion = self.mostrar()
14
            else:
15
                opcion = self.mostrar
16
            if opcion:
17
                return f(*args, **kw_args)
18
            else:
19
                return "Nop"
20
        none.__name__ = f.__name__
21
        none.__doc__ = f.__doc__
22
        return none
23
 
24
@forat_negre_sonat(mostrar = lambda :random.choice((True, False)))
25
def suma(a, b):
26
    "Suma dos elements que li passam com a paràmetre"
27
    return a+b
28
 
29
@forat_negre_sonat(mostrar=True)
30
def resta(a,b):
31
    return a-b
32
 
33
if __name__=="__main__":
34
    print "Exemple amb %s " % suma.__name__
35
    print suma(2,3)
36
    print suma(5,6)
37
    print suma(9,5)
38
    print "Exemple amb %s " % resta.__name__
39
    print resta(2,3)
40
    print resta(5,6)
41

El método complejo de crear decoradores con parámetros

01
def forat_negre_dos(mostrar):
02
    def wrap(f):
03
        @wraps(f)
04
        def wrapped_function(*args, **kw_args):
05
            if callable(mostrar):
06
                opcion = mostrar()
07
            else:
08
                opcion = mostrar
09
            if opcion:
10
                return f(*args, **kw_args)
11
            else:
12
                return "Nop"
13
        return wrapped_function
14
    return wrap
15

Bien, enrevesado, lo que se dice enrevesado no lo es, ya que una cosa tan simple no tiene demasiada historia, pero fijaos que el codigo se sigue bastante peor.

El primero que hemos hecho es definir nuestra función, donde hemos puesto los parámetros que admite. Esta función devuelve otra función que admite un argumento, que es la función a decorar, que a su vez admite un número indeterminado de argumentos (recordemos que esto lo estamos forzando nosotros).

Cómo la segunda función, `wrapped_function` está definida dentro de `wrap`, tiene acceso al parámetro del decorador y puede utilizarlo.

Encadenando decoradores

Los decoradores se pueden encadenar, es decir, una función puede tener tantos decoradores como haga falta y necesitemos, sólo limitados por nuestro sentido común y la legibilidad del programa. Dos decoradores son habituales, tres no se ven mucho, cuatro o más son para pensárselo.

Para el ejemplo tomaré prestado Python Decorator Library uno de los decoradors más útiles, el memoize, que nos permite cachear una función a partir de sus parámetros. La implementación que hace Python Decorator Library del patrón memoize bastante es sencilla de seguir con lo que ahora sabemos y además nos servirá para completar la construcción de decoradores sin parámetros usando una clase.

01
class memoized(object):
02
   """Decorator that caches a function's return value each time it is called.
03
   If called later with the same arguments, the cached value is returned, and
04
   not re-evaluated.
05
   """
06
   def __init__(self, func):
07
      self.func = func
08
      self.cache = {}
09
   def __call__(self, *args):
10
      try:
11
         return self.cache[args]
12
      except KeyError:
13
         self.cache[args] = value = self.func(*args)
14
         return value
15
      except TypeError:
16
         # uncachable -- for instance, passing a list as an argument.
17
         # Better to not cache than to blow up entirely.
18
         return self.func(*args)
19
   def __repr__(self):
20
      """Return the function's docstring."""
21
      return self.func.__doc__
22

A diferencia de la construcción con parámetros, en el constructor de la clase memoized se pone como parámetro la función a decorar, y al método __call__ los parámetros de la función, en lugar de la función a decorar como se hacía al otro método.

¿Por qué se ha usado esta manera si la otra es más sencilla? Pues porqué necesitamos mantener en memoria la caché y lo que se hace es mantenerla en un diccionario dentro de la misma clase. Si la caché fuera externa (con memcached por ejemplo), se habría podido hacer perfectamente en forma de función.

Además definiremos un decorador que nos servirar para indicar cuando entramos a la función y comprobar el decorador memoized.

01
def log(f):
02
    "Registra l'execució de la funció"
03
    def wrap(*args):
04
        print "Excutant %s, args: %s" % \\
05
           (f.__name__, ",".join(str(x) for x in args))
06
        return f(*args)
07
    return wrap
08
 
09
@memoized
10
@log
11
def fibonacci(n):
12
    "Return the nth fibonacci number."
13
    if n in (0, 1):
14
        return n
15
    return fibonacci(n-1) + fibonacci(n-2)
16
 
17
print fibonacci(12)
18

Probad de ejecutar este código con y sin la función memoized. Con los dos decoradors activos veréis que cada decorador toma como entrada la función ya decorada que sale del decorador que tiene más abajo. Así el memoized coge como entrada la función fibonacci ya decorada con el log.

Podéis hacer la prueba con un ejemplo más simple:

01
#!/usr/bin/env python
02
# -*- coding: UTF-8 -*-
03
 
04
def uppercase(f):
05
    "Dada una función f que devuelve un string lo pasa todo a mayúsculas"
06
    def wrap():
07
        return f().upper()
08
    return wrap
09
 
10
def make_bold(f):
11
    "Dada una función f que devuelve un string le añade los tags de bold"
12
    def wrap():
13
        return "<strong>%s</strong>" % f()
14
    return wrap
15
 
16
@make_bold
17
@uppercase
18
def say_hello():
19
    return "Hello world"
20
 
21
print say_hello()
22

Probad cambiando la orden de los decoradores y veréis perfectamente como se van aplicando éstos desde la función hacia arriba. En el ejemplo primero se convierte el “Hello word” a mayúsculas y después se le aplican los tags de negrita.

La signatura pendiente

Antes de acabar nos queda un tema pendiente: la firma. Los decoradors que hemos creado pueden preservar el nombre y la documentación de la función que decoran, pero no preservan la firma, es decir, el número de parámetros que le pasamos.

Michele Simionato ha escrito un módulo excelente llamado *decorator* que extiende la utilizació de los decoradores, mateniendo la firma de la función, el nombre y la documentación, y además nos da la posibilidad de crear factorías de decoradors. Una herramienta para tener siempre a mano. Con este módulo podríamos escribir el código del ejemplo anterior cómo:

01
from decorator import decorator
02
@decorator
03
def uppercase(f, *args):
04
    "Donada una funció f que retorna un string ho passa a majúscules"
05
    return f(*args).upper()
06
 
07
@decorator
08
def make_bold(f, *args):
09
    "Afegeix el tag strong a la sortida de la funció"
10
    return "<strong>%s</strong>" % f(*args)
11
 
12
@uppercase
13
@make_bold
14
def say_hello(nom):
15
    "Di hola, home!"
16
    return "Hello world %s" % nom
17
 
18
if __name__=="__main__":
19
    from inspect import getargspec
20
    print say_hello('World')
21
    print say_hello.func_name
22
    print say_hello.__doc__
23
    print getargspec(say_hello)
24

Si ejecutáis el código podréis ver que no nos ha hecho falta recurrir a wraps o a reasignar el nombre, la propia librería de Simionato lo ha hecho. Además, si nos fijamos en la salida del ejemplo:

HELLO WORLD WORLD
say_hello
Di hola, home!
ArgSpec(args=['nom'], varargs=None, keywords=None, defaults=None)

La primera línea corresponde a la salida de la función que hemos decorado. La segunda es el nombre de dicha función. Vemos el nombre de la función original y no la del decorador. La documentación también se ha mantenido y para acabar, podemos ver que la firma de la función es correcta, nos dice que tiene un argumento obligatorio llamado `nom`.

Conclusión

Espero haber dejado un poco más claro el tema de los decoradores. Crearlos no es difícil, utilizarlos es todavía más simple, sólo tenemos que tener claro qué son y cuando usarlos. Son una herramienta potente que nos permite hacer nuestro código más legible y cohesionado. Sin miedo y a disfrutar con los decoradores!.

Cómo todo en esta vida, usadlos con sentido común y moderación.

Referencias

Para escribir este artículo me he basado en distintas fuentes, las más interesantes las cito a continuación:

PEP 318
Decorators I : Introduction to Python Decorators
Decorators II: Decorator Arguments
Python Decorators
Understanding decorators
Charming Python: Decorators make magic easy
Decorator 3.1.2
Decorator Pattern
Python decorator Library

Posted in