Archive for the 'Python' category

Descriptores – Parte 1

jun 19 2011 Published by under Python

Cuando accedemos a los atributos de un objeto en python, a veces existen unos intermediarios casi imperceptibles llamados “descriptores” que son los responsables últimos del funcionamiento de la programación orientada a objetos. Están detrás de propiedades, métodos, métodos estáticos, métodos de clase y del mecanismo super() responsable de la herencia múltiple. Su labor es imprescindible y, sin embargo, son los grandes desconocidos del lenguaje.

Protocolo “descriptor”

Por protocolo “descriptor” se entiende la sustitución de un atributo por un objeto que intermedia en los accesos a ese atributo. Tal vez, las propiedades (property) puedan ser el ejemplo más visible de los descriptores, pero veremos que los descriptores están más presentes de lo podemos pensar.

Como descripción formal del protocolo descriptor, podemos decir que un descriptor es todo objeto que tenga definido al menos uno de estos tres métodos:

descr.__get__(self, obj, type=None) --> value

descr.__set__(self, obj, value) --> None

descr.__delete__(self, obj) --> None

Respectivamente, serían los métodos para obtener, asignar y borrar un atributo del objeto obj.

Podemos distinguir dos tipos de descriptores:

  • Descriptor de datos (data descriptor): cuando tiene definidos los métodos __get__ y __set__. Es el que usaremos para acceder y cambiar el valor de un atributo.
  • Descriptor de no-datos (non-data descriptor): cuando sólo tiene definido el método __get__. Su uso será casi exclusivo para acceso a los métodos de un objeto.

Como veremos más adelante, distinguir entre estos dos tipos de descriptores es muy importante, ya que cada uno tiene distinto orden de preferencia cuando se buscan atributos en una jerarquía de clases.

Implementación de los “Descriptores de Datos”

Empecemos por un ejemplo:

class Desc(object):
    def __init__(self, mul):
        self.mul=mul
    def __get__(self, obj, cls=None):
        return obj.value*self.mul

class C(object):
    a12=Desc(12)
    a200=Desc(200)
   
    def __init__(self,value):
        self.value=value

c=C(2)
print c.value, c.a12, c.a200  #--> 2 24 400

Los atributos a12 y a200 están definidos por instancias del descriptor Desc(). Cuando accedemos a estos atributos, en lugar de devolvernos el descriptor, nos devuelve el valor resultante del método __get__ del descriptor.

De modo más explícito, sería:

c.a12 --> c.a12.__get__(c)

Al no estar definido el método __set__, se pueden reasignar estos atributos sin mayor problema, aunque dejarían así de estar controlado por el descriptor:

c.a12=12

Para completar el protocolo de descriptor de datos basta añadir un método __set__:

class Descrip(object):
    def __init__(self, mul):
        self.mul=mul
    def __get__(self, obj, cls=None):
        return obj.value*self.mul
    def __set__(self, obj, value):
        obj.value=value

La asignación anterior, se nos convertiría en:

c.a12=12 --> c.a12.__set__(c, 12)

Como se intuye, el descriptor tiene aquí total control sobre el valor final que se guardará como atributo. Como posible utilización, se pueden crear atributos de sólo lectura, para lo que bastaría con que el método __set__ genere un error AttributeError si se intenta modificar el atributo:

class Descrip(object):
    def __init__(self, mul):
        self.mul=mul
    def __get__(self, obj, cls=None):
        return obj.value*self.mul
    def __set__(self, obj, value):
        raise AttributeError

Tan sólo falta añadir el método __delete__ para completar el protocolo. No hay que olvidarse de este método si queremos que un atributo de sólo lectura aún pueda ser modificado mediante un borrado previo a su reasignación:

class Descrip(object):
    def __init__(self, mul):
        self.mul=mul
    def __get__(self, obj, cls=None):
        return obj.value*self.mul
    def __set__(self, obj, value):
        raise AttributeError
    def __delete__(self, obj):
        del self

c=C(2)

print c.a12 #--> 24

c.a12=100 #ERROR: AttributeError

del C.a12
c.a12=100

print c.a12  #--> 100 (no descriptor)

Saltarse al descriptor

Llegados aquí, se nos plantea una pregunta: ¿hay algún modo de acceder a los atributos sin pasar por su descriptor?

Y no es para nada una pregunta caprichosa. El descriptor necesita algún modo de acceder a los atributos que está gestionando sin tener que pasar por sí mismo. Tal vez, se podría hacer a través del diccionario del objeto, accesible como __dict__:

c.__dict__["a12"]=100  #equivalente a c.a12=100

Si lo pruebas, verás que no funciona. Cuando se busca un atributo, primero se busca entre los atributos de la clase antes de mirar en el diccionario de la instancia. Este orden de prioridades lo veremos en el próximo artículo cuando veamos el funcionamiento interno de un descriptor.

One response so far

Último item de un iterable

jun 06 2011 Published by under Python

Algunas veces necesitamos obtener el último item de un iterador. Para ello se suele iterar hasta agotar el iterador:

for it in iterador:
    pass

last_item=it

Una alternativa que se ve bastante es convertir previamente el iterable en una lista:

last_item=list(iterador)[-1]

Tiene el incoveniente de gastar recursos inultilmente al crear una lista de la que sólo nos interesa su último elemento.

En stackoverflow se pueden ver algunas respuestas a este problema, pero ninguna me convence lo suficiente.

Aquí pongo mi solución, simple y elegante donde las haya:

last_item=max(enumerate(iterador))[1]

One response so far

Estudio función factorial

jun 06 2011 Published by under Matemáticas, Python

Hace un tiempo me dió por recopilar distintas funciones en python para calcular el factorial. Aquí van todas, algunas bastante curiosas. Si conoces algún tipo más, no dejes de añadirla en los comentarios.

Versión recursiva

Todo programador ha tenido que ver esta definición como ejemplo de funciones recursivas :

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

Se podría hacer algo más compacta usando el operador ternario:

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

Como toda función recursiva en python, existe el peligro de que nunca termine la función. Es el motivo por el que python tiene fijado un límite de recursividad dado por sys.getrecursionlimit(), que por defecto es de 1000 invocaciones recursivas o, lo que es lo mismo, que no podamos calcular factoriales mayores de 1000.

Podemos incrementar el límite con sys.setrecursionlimit(n), pero seguirá siendo una solución provisional. Lo mejor es pasarnos a una solución “iterativa”.

Versión iterativa

También es una de la funciones más conocidas por todo programador:

def fact(n):
    res=1
    for i in xrange(1,n+1):
        res*=n
    return res

Normalmente, todo lenguaje tiene un límite en el tamaño de un entero que hace que esta función no pueda calcular factoriales muy grandes. Pero python tiene la característica de pasar de entero a entero largo cuando así lo requiera la operación, lo que hace que se puede calcular cualquier número factorial, con el único límite de tiempo para calcularlo. Por lo general, con número grandes cuesta menos calcular el factorial que imprimirlos en pantalla.

Versión aproximada (función de Stirling)

Para número muy grandes, existe una aproximación llamada “Aproximación de Stirling” que se suele usar en mecánica estadística.

import math
def fact(n):
    return math.sqrt(2*math.pi*n)*math.pow(n/math.e,n)

Lamentablemente, los números reales (tipo double) son aquí una limitación de tamaño, por lo que no podemos hacer cálculos para números altos (precisamente, para los que teóricamente iba mejor esta función).

Versiones one-line

Muchas veces, los programadores se toman como reto poder expresar una fórmula compleja en una sóla línea, de modo que se pueda sustituir la llamada a la función por la definición de esta directamente. Son las llamadas funciones “oneline”.

reduce(lambda x,y:x*y,xrange(1,n+1),1)

Podemos aprovechar que tenemos el operador multiplicación y con ello evitar la función lambda (últimamente, en desuso):

import operator
reduce(operator.mul, xrange(1,n+1),1)

Algo más bizarro, evitando lambda y reduce:

[j for j in [1] for i in range(2,n+1) for j in [j*i]][-1]

Esta versión es en realidad un “reduce sin usar reduce”. Para entender cómo funciona, lo mejor es verlo como varios fors anidados:

def fact(n):
    for j in [1]:
        for i in range(2,n+1):
            for j in [j*i]:
                yield j

res=list(fact(n))[-1]

El primer for tan sólo sirve para dar una valor inicial a la variable j, y el tercer for sería el equivalente “oneline” de j=j*i.

En realidad, esta función no está muy optimizada ya que mantiene en memoria la lista completa de todos los resultados intermedios. Un modo más inteligente de usar esta expresión sería como un iterador, donde los resultados intermedios ya no son almacenados:

for res in (j for j in [1] for i in range(2,n+1) for j in [j*i]):
    pass

Aunque funciona perfectamente, no se puede considerar como función de una sóla línea. Para conseguirlo, tenemos que ir a algo totalmente críptico, incluyendo reduce y lambda, que acabaría siendo el siguiente engendro:

reduce(lambda x,y:y,(j for j in [1] for i in range(2,n+1) for j in [j*i]))

Actualización: Si queremos alguna solución sin reduce ni lambda, teniendo en cuenta que la función factorial es incremental, podemos emplear max() para obtener una versión con iterador de una sóla línea:

max(j for j in [1] for i in range(2,n+1) for j in [j*i])

¿Se os ocurren otras formas de expresar el factorial en una sóla línea?

6 responses so far

Instalación cx_Oracle para ia64

jun 04 2011 Published by under Python, Técnicas

Itanium, un sistema ¿obsoleto?

Últimamente, algunos grandes de la informática como Microsoft, Oracle y RedHat han determinado que los sistemas Itanium han quedado obsoletos con lo que dejarán de darles soporte, aunque hace sólo unos pocos años estos sistemas de 64bits se ofertaban al mercado como el futuro de los sistemas servidores empresariales.

En este punto, me encuentro que tengo en mi trabajo algunos servidores Itanium II que, lejos de considerarlos obsoletos, me parecen perfectos para alojar en ellos algunos de los proyectos python desarrollados en plone o django. Con la reciente salida de la distribución Debian “Squeeze”, y con la ayuda de un alumno que vino a hacer sus prácticas con nosotros, me animé a sustituir el Linux RedHat que se había quedado sin mantenimiento por una la última versión ia64 de debian. Esta versión es algo más limitada en paquetes que las versiones para arquitecturas i686 y amd64, pero con un poco de esfuerzo es posible completar la instalación compilando paquetes a partir de los fuentes. Y puedo afirmar que ha sido todo un éxito. Vuelvo a tener un sistema potente, completo y, sobre todo, mucho más libre.

La idea de este artículo es contar cómo instalar y configurar, en debian para itanium, del cliente de oracle y el conector cx_Oracle para python.

Instalación cliente oracle

Lo primero es descargar desde la web de oracle del cliente. Para ello hay que descargar los siguiente paquetes para itanium, previo registro gratuito:

basic-10.2.0.4.0-linux-ia64.zip
sdk-10.2.0.4.0-linux-ia64.zip
sqlplus-10.2.0.4.0-linux-ia64.zip

jdbc-10.2.0.4.0-linux-ia64.zip
odbc-10.2.0.4.0-linux-ia64.zip

Los dos últimos son opcionales, pero siempre pueden venir bien guardarlos por si hacen falta en el futuro con alguna aplicación (el conector jdbc nos vendrá bien para usarlo con jython).

Se decomprimen estos paquetes en el mismo directorio y obtendremos un único directorio llamado:

instantclient_10_2

Movemos este directorio a un lugar adecuado, por ejemplo a:

/opt/oracle/instantclient_10_2

No olvidar darle permisos adecuados, sobre todo si queremos que el servidor apache (mod_wsgi) pueda acceder a él (puedo asegurar que se pierde mucho tiempo hasta que averiguas este fallo tan tonto):

# chmod +rx /opt/oracle/instantclient_10_2/

Cuando pasemos a compilar cx_Oracle, veremos algunos fallos por no ser capaz de encontrar algunas librerías compatidas. Para evitarlo, debemos crear algunos enlaces:

# cd /opt/oracle
# ln -s libclntsh.so.10.1 libclntsh.so
# ln -s libclntsh.so.10.1 libclntsh.dylib

Ahora tenemos que actualizar las referencias a la libreras compartidas. Creamos el fichero /etc/ld.so.conf.d/oracle.conf con la siguiente línea:

/opt/oracle/instantclient_10_2

Y actualizamos:

# ldconfig

Para comprobar que funciona bien, podemos probar la utilidad sqlplus a ver si conectamos. Esta utilidad viene dentro del directorio.

Para poder compilar el paquete cx_Oracle se necesita unas cuantas variables de entorno que meteremos en el.profile`:

    export ORACLE_HOME="/opt/oracle/instantclient_10_2"
    export DYLD_LIBRARY_PATH="$ORACLE_HOME"
    export SQLPATH="$ORACLE_HOME"
 
    export PATH="$PATH:$ORACLE_HOME"

Para instalar el paquete cx_Oracle, podemos instalar antes el paquete python-pip que nos ofrece la utilidad pip que nos hará más fácil la instalación:

# apt-get install python-pip
# pip install cx_Oracle

Con ésto se debería descargar, compilar e instalar cx_Oracle. Saldrán algunas advertencias que podemos ignorar. Si todo ha salido bien, podemos pasar a probar si podemos importar el módulo:

    # python
    Python 2.6.6 (r266:84292, Dec 27 2010, 21:05:55)
    [GCC 4.4.5] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import cx_Oracle
    >>>

Con el módulo instalado, lo he probado desde el “backend” de oracle para django y todo funciona a la perfección.

One response so far

Porqué uso jython

may 30 2011 Published by under jython

Python de sabores

Cuando hablamos de python, normalmente nos referimos a su versión canónica implementada en lenguaje C, también conocida por “CPython”. Toda la evolución del lenguaje se realiza alrededor de esta implementación y pocas veces se piensa en otras implementaciones. Pero una de las características de python es ser multiplaforma, y lo demuestra con implementaciones para varias plataformas. Algunas de las implementaciones más interesantes serían:

  • Jython: Implementación para JVM que se integra y hace uso de la numerosísismas librerías Java y entornos J2EE. Combina un entorno robusto que rodea a Java con la programación dinámica de python.

  • IronPython: Una de las implementaciones para la plataforma .Net y mono. Se integra con el framework .Net de Microsoft, llegando a una eficiencia bastante cercana al lenguaje C#.

  • PyPy: Un python escrito en python. Su objetivo es librar a python de las limitaciones impuestas por el lenguaje C, dando lugar a una implementación puramente python.

Cada una de estas implementaciones lleva su propio ritmo de desarrollo, siguiendo la estela de CPython. En estos momentos, el lenguaje python (CPython) está parado debido a la moratoria PEP-3003 de dos años, a punto de terminar, que se está aprovechando para acercar a CPython el resto de implementaciones y así unir las distintas comunidades de desarrolladores en el siguiente avance de Python hacia la versión 3.

Python con sabor Java

De entre todas la implementaciones de python, la que uso habitualmente en mi trabajo es jython. Sin entrar en polémicas sobre si un lenguaje es mejor que otro, cuando uno se decide por un lenguaje híbrido como jython lo hace desde el convencimiento de que la mejor solución consiste en usar lo bueno de ambos mundos. Por un lado, los entornos java ofrecen robustez y librerías bien probadas para cualquier aplicación empresarial; por otro lado, jython ofrece técnicas de programación dinámica que mejoran la productividad.

Entrando en detalle, este sería un listado de ventajas e incovenientes de usar jython sobre python y/o java:

Multiplataforma

Aunque python ya venga instalado en prácticamente todo sistema linux o macintosh, o incluso aparezca empotrado en aplicaciones como openoffice/libreoffice, no siempre es posible controlar qué versiones, módulos y librerías hay instalados en el sistema. La disparidad de sistemas y configuraciones hace inviable contar con un entorno homogéno para ejecutar nuestro programa python. Por lo general se desarrolla con una configuración fijada, con la esperanza de que el sistema de producción cuente con la misma configuración.

Jython se aprovecha la difusión de la máquina virtual JVM en casi todos los sistemas. Esta máquina virtual nos crea una capa de abstracción que facilita el traslado de la misma configuración de nuestro entorno de desarrollo al sistema de producción, tan fácil como copiar un fichero de un sistema a otro.

Además resulta sencillo crear un entorno jython portable1 en un pendrive o un disco duro externo, con lo que podemos llevarnos nuestro entorno de desarrollo con nosotros.

Velocidad y memoria

Existe cierta idea equivocada que los programas java son lentos y consumen mucha memoria. En realidad, java se inventó para sistemas empotrados, como demostraría las aplicaciones y juegos para existen para teléfonos móviles. Hoy en día, una aplicación para java es bastante rápida una vez arrancada la máquina virtual, y el consumo de memoria puede delimitarse para no agobiar al resto del sistema.

Así mismo, el aspecto gráfico de las aplicaciones java es muy similar a las aplicaciones nativas, disponiendo de interfaces de bajo nivel para control gráficos y dispositivos de entrada y salida.

Sincronismo multihilo

Con el tiempo, la gestión de los hilos de ejecución en una máquina virtual JVM ha llegado a superar cualquier otra implementación gracias a la capa de abstracción que impone. En C, es una labor del programador realizar esta gestión a mano o, con algo de suerte, disponga de alguna librería que facilite el sistema operativo donde se vaya a ejecutar la aplicación, no consiguiendo toda la estabilidad que sería deseable (un ejemplo sería la inestabilidad de algunas extensiones de apache frente a la robustez de los servidores de aplicaciones web para java).

Por ello jython delega esta gestión de hilos delegando en el JVM e, incluso, delega en él la recolección de basura (garbage collection). Como consecuencia directa, en jython no existe el odioso GIL de CPython que impide que dos hilos se ejecuten simultáneamente.

Base de datos

La conectividad con bases de datos en Jython se realiza mediante los conectores JDBC de java, que es una especie de estándar en java que toda base de datos ofrece. En jython, gracias al grandioso módulo zxJDBC podemos usar conexiones jdbc del modo habitual en python, o sea, mediante la DB-API2. La ventaja es que sólo necesitamos añadir el conector jdbc (un fichero .jar) a la ruta de clases del JVM, sin tener que instalar la librería o todo el cliente completo como exigen algunas bases de datos en CPython.

En mi caso concreto, necesito conectarme a varios tipos de bases de datos diferentes para interoperar entre ellos. Me resultaba complejo tener que ir instalando las librerías de conexión para el sistema, con algunos pidiendo que te instales el cliente completo con licencia. Además, tenía que instalar los módulos de python, que no siempre estaban actualizados o, simplemente, no existían. Con jython, tengo todos los conectores jdbc en un directorio y con sólo un módulo, zxJDBC, tengo todo lo necesario.

¿Y sqlite? Python incluye sqlite como base de datos sencilla. Al estar programada en C no aparece con jython. Como alternativa, podríamos usar la javadb (aka derbydb) que se suele instalar junto con el java, aunque es algo de lo que no podemos fiarnos. Mi recomendación es usar h2. En un fichero de poco más de 1 Mb tenemos una implementación completa de base de datos relacional (SQL-92), tremendamente rápida en comparación con otras, que tiene acceso a través del sistema de ficheros al estilo sqlite o acceder en compartido como servidor TCP/IP, con interface de línea de comandos, con su administrador web,… Es ideal para hacer de intermediario entre bases de datos, aceptando enlaces JDBC a otras bases de datos e importaciones/exportaciones en formato CSV.

Contendores Java

Otro aspecto interesante son los contendores para aplicaciones java (J2EE). Una aplicación jython puede, del mismo modo que hace java, desplegarse en estos contenedores para aprovechar sus servicios j2ee como sería un pool de conexiones para una base de datos. Impresionante, resulta ver que con la versión 3 de glassfish ya se incluye un contenedor específico para jython, lo que permite desplegar en él aplicaciones desarrolladas en django ó pylons (éste último todavía en fase de pruebas) sin cambiar una sóla línea de código.


(CONTINUARÁ)

Espero haber conseguido interesarte con este artículo. Mi intención es continuar hablando de jython en próximos artículos y mostrar su uso en desarrollos de todo tipo. Nos veremos pronto.


  1. En próximos artículos veremos cómo usar virtualenv para crear estos entornos portables. 

3 responses so far

Python in tasca

may 24 2011 Published by under Python

Es curioso lo que se parece python a php en este libro :-) )

Python in Tasca

Supongo que se trata de la versión italiana del libro “Python Phrasebook” de Brad Dayley

One response so far

qtm con reStructuredText

may 24 2011 Published by under Python, Técnicas

Puestos a explotar todas las posibilidades de la utilidad qtm, también es posible emplear esta utilidad para previsualizar y subir al blog artículos escritos en reStructuredText (abreviadamente ReST) en lugar de markdown que viene por defecto.

De la instalación de docutils obtendremos las herramientas básicas con la que crear documentación para python. Una de estas herramientas es rst2html, con la que podríamos convertir un artículo en ReST a html antes de subirlo. El problema es que esta conversión se realiza como documento completo, con las secciones <head> y <body> típicas de un documento html, lo que no va bien si queremos insertar la salida de este comando a la entrada de nuestro blog.

Como solución, podríamos crear una plantilla para rst2html que sólo contenga la parte del <body>, pero hay una alternativa mejor: usar el script rst2wp de Matthias Friedrich. Colocamos este script en un directorio apropiado (eg: /home/usuario/bin) y configuramos qtm de la siguiente manera:

qtm para ReST

Como ya comenté en anteriores artículos, para escribir en páginas web resulta más sencillo usar markdown o markdown extra, pero a veces no queda más remedio que emplear ReST cuando necesitamos escribir documentación técnica en determinados blogs. Con qtm tenemos una herramienta sencilla para estos cometidos.

One response so far

Abierto repositorio mercurial

abr 19 2011 Published by under Python

Hace mucho que estoy usando mercurial para control de versiones distribuido (DCVS). Abrí una cuenta en bitbucket, pero hasta ahora no le había sacado mucho provecho.

Aprovechando que el inicio de este blog, me he propuesto empezar a compartir proyectos y código a través de bitbucket. Para quien no lo sepa todavía, bitbucket es un servicio donde compartir repositorios de mercurial, muy usado sobre todo en python y que sería similar a lo que hace github con git.

Mi lugar en bitbucket será: http://hg.ch3m4.org

Como prueba, he empezado a subir algunos módulos, como el código del reto PET1 que comenté en un artículo anterior: http://hg.ch3m4.org/pystore/src/tip/pet1-pych3m4.py

Si tenéis cuenta en bitbucket y queréis publicar la URL de alguno de los ficheros (tal como lo he hecho antes) sólo tenéis que seguir el siguiente truco: busca el fichero en la pestaña “Sources”, copia la dirección y sustituye el código de revisión por la palabra tip para indicar que siempre se vaya a la versión más reciente del código.

ACTUALIZACIÓN: según leo en la documentación de bitbucket existe una característica no documentada de bitbucket que posibilita mantener una colección de ficheros estáticos como si fuera una página web. Para probar, he creado un repositorio hg llamado chemacortes.bitbucket.org, que será la dirección de mi páqina web en bicbucket. He metido en este repositorio el fichero python anterior, con lo que ahora es más fácil localizarlo como http://chemacortes.bitbucket.org/pet1-pych3m4.py.

No responses yet

Podcast Z #2: Animalario con lenguajes dinámicos

mar 24 2011 Published by under Python

Acaba de salir el número 2 de Podcast Z de Jesús Cea y Pablo Lobariñas. En este episodio se tratan diversas técnicas de adaptación de código externo aprovechando la particularidades propias de lenguajes dinámicos como python. Poniendo un caso real como ejemplo, se van tratando los distintos enfoques dinámicos (en tiempo de ejecución) como “monkey patching” o “duck typing” que permiten derivar código sin necesidad de emplear la herencia propia de la programación orientada a objetos. Un podcast totalmente recomendable, sobre todo si estás pensando que un lenguaje dinámico no ofrece ninguna ventaja con respecto a los lenguajes estáticos.

Próximamente dedicaré algunas entradas en este blog dedicadas a estas técnicas dinámicas, juntándolas con otras similares que tenía previstas.

Más info: Podcast Z #2: Animalario con lenguajes dinámicos

No responses yet

Desafío PET1

mar 14 2011 Published by under Python

Hace mucho tiempo, el grupo de programadores Python de Argentina publicaron el primer número de la revista PET: Python Entre Todos

En ese primer número se proponía el siguiente desafío PET:

Escribir un programa que reciba un número en la entrada estándar e imprima por pantalla la factorización del número. Los siguientes ejemplos muestran posibles entradas y cómo se espera que sean las salidas.
entrada: 11
salida: 11

entrada: 8
salida: 2^3

entrada: 24
salida: 2^3 x 3

entrada: 168
salida: 2^3 x 3 x 7
Notar que los factores se ordenan en orden creciente y si un factor aparece más de una vez, se debe expresar en forma de potencia. Los participantes deben enviar su solución como un archivo .py y esta será ejecutado con python 2.7. El ganador del desafío será aquel que logre la solución con la menor cantidad de caracteres.

Desconozco el motivo, pero a fecha de hoy todavía no se ha dado la solución. El cuadro de honor de las mejores soluciones se puede ver aquí donde (¡Oh, sorpresa!) estoy en cabeza.

Para todos los que estén aún esperando la solución de 111 caracteres, aquí va la mía:

n=input();d=1;r=""
while d<n:
 d+=1;s=0
 while n%d<1:n/=d;s+=1
 if s:r+=" x %d"%d+"^%d"%s*(s>1)
print r[3:]or n

Para medir bien el número de caracteres, hay tener en cuenta que se guarda con fin de línea estilo unix, sin EOF y sin tabuladores (se evita tener dos espacios seguidos). Todavía espero una solución mejor. Si te fijas, el or de la última línea está pegado con el primer argumento. Es un truco muy sucio que no debería haberlo permitido el intérprete.

Aquí tienes el fichero para descargar: pet1-pych3m4.py

One response so far

« Newer posts