Automágica: durante 2017 estoy trabajando bastante en Automágica, mi software para editar libros: Más información - Posts relacionados

Dulzuras en el cumple de Mamá!

El jueves fue el cumple de Mamá y por una feliz coincidencia no tuve que trabajar, por lo que viajé a saludarla. Todos los años prepara chocolate para las amigas que pasan a saludarla a la tarde. Mi hermana preparó un montón de exquisiteces dulces. Estas son algunas (lemon pie, lingote de chocolate blanco, muffins de coco, limon, quinotos, chocolate y nuez):

[gallery link="file"]

Pidan recetas!


Decoradores en Python (II) - Decoradores con parámetros

Un año después del primer artículo, llega el segundo. ¿Por qué tardó tanto? Por lo general mis artículos técnicos surgen de algún problema que se me presenta y para el cual necesito investigar antes de poder solucionarlo. El artículo anterior cubría todo lo que necesité hacer con decoradores en Python hasta el mes pasado, cuando necesité decoradores con parámetros.

Si no leíste el artículo anterior, te recomiendo que lo hagas antes de seguir: Decoradores en Python (I).

Decoradores con Parámetros

Cuando quise escribir un decorador con un parámetro me encontré con errores que ni siquiera entendía. No solo que los estaba escribiendo mal, sino que también los estaba usando mal. Te voy a evitar el sufrimiento.

Un decorador con parámetro se aplica así (siendo deco un decorador y 1 el argumento utilizado):

@deco(1)

def funcion_a_decorar(a, b, c):

    pass

Creo que la raíz de mi confusión fue el azúcar sintáctica (si, el @). Así que vamos a sacarlo y ver cómo se usaría este decorador en una versión de Python más vieja:

def funcion_a_decorar(a, b, c):

    pass
funcion_a_decorar = deco(1)(funcion_a_decorar)

Esto luce más claro para mi: deco es llamado con un argumento y el resultado tiene que ser algún objeto que pueda ser llamado con una función como parámetro para... decorarla. ¿Se entiende la idea? Vamos a definir deco, va a recibir un parámetro y utilizarlo para crear un decorador como los del artículo anterior. Finalmente retorna este decorador interno.

Agreguemos semántica al ejemplo. Mi decorador con parámetro recibirá un número, este número se usará para indicar cuantas veces queremos ejecutar la función decorada.

def deco(i):

    def _deco(f):

        def inner(*args, **kwargs):

            for n in range(i):

                r = f(*args, **kwargs)

            return r

        return inner

    return _deco

Como una convención personal, uso para el nombre de la segunda función _{nombre de la primer funcion}. Notemos entonces que _deco es un decorador dinámico, dependiendo del parámetro i, la función inner se compilará de una forma o de otra. Apliquemos el decorador:

@deco(2)

def saluda(nombre):

    print "hola", nombre
>>> saluda("juanjo")

hola juanjo

hola juanjo
@deco(3)

def suma1():

    global n

    n += 1
>>> n = 0

>>> suma1()

>>> n

3

Cuando aplicamos deco, se ejecuta deco, se compila _deco, se aplica _deco a la función que definimos y se compila inner utilizando un valor dado para i. Cuando llamamos a nuestra función (saluda, o suma1, en los ejemplos) se ejecuta inner.

¡Espero que se haya entendido!

Si no...

Si en lo anterior no fui lo suficientemente claro (por favor quejate en un comentario), no todo está perdido. Te puedo entregar un decorador para decoradores que convierte a tu decorador en un decorador con parámetros. ¿Qué tal?

def decorador_con_parametros(d):

    def decorador(*args, **kwargs):

        def inner(func):

            return d(func, *args, **kwargs)

        return inner

    return decorador

Original usando lambda en http://pre.activestate.com/recipes/465427/

Se usa así:

@decorador_con_parametros

def deco(func, i):

    def inner(*args, **kwargs):

        for n in range(i):

           r = func(*args, **kwargs)

        return r

    return inner
@deco(2)

def saludar(nombre):

    print "chau", nombre
>>> saludar("juanjo")

chau juanjo

chau juanjo

Para la próxima

Para el próximo artículo voy a explorar utilizar clases decoradoras en lugar de funciones decoradoras. Si bien todavía no lo terminé de investigar, me parece un enfoque que permite escribir código más organizado. Veremos! update: aquí está.


Galletitas de Avena y Manzana

Estas masitas las hizo hoy a la mañana la Mary. Saben a barritas de cereal. A mi me gustan sin manzana.

Ingredientes:

    <li> 1 taza de puré de manzanas</li>
    
    <li> 1 taza de azúcar</li>
    
    <li> 1/2 taza de nueces picadas</li>
    
    <li> 1/2 taza de pasas de uvas</li>
    
    <li> 4 tazas de Avena</li>
    
    <li> 1 cucharadita de esencia de vainilla</li>
    
    <li>1 pizca de sal</li>
    

    Imagen215

    Preparación

    Mezclar todos los ingredientes hasta que quede una pasta homogénea. Armar las galletas con una cuchara y colocarlas sobre una asadera rociada con Fritolín. Hornear aproximadamente 30min a horno moderado hasta que queden secas.

    Imagen216

    Imagen217

    Imagen218

    Rinde 32 galletas



    La historia de Python: El problema con la división entre enteros

    El siguiente texto es una traducción del artículo The Problem with Integer Division de Guido van Rossum publicado en http://python-history.blogspot.com/.

    El problema con la división entre enteros

    La forma en que Python maneja la división entre enteros es un ejemplo de errores iniciales con enormes consecuencias. Como se mencionó anteriormente, cuando Python fue creado abandoné el enfoque con el que ABC abordaba los números. Por ejemplo, en ABC, cuando dividías dos enteros, el resultado era un número racional exacto que representaba el resultado. En Python, sin embargo, la división entre enteros trunca el resultado a un entero.

    En mi experiencia, los números racionales no son tan buenos como los diseñadores de ABC esperaban. Una experiencia típica podría ser escribir un programa simple con alguna aplicación comercial (digamos, calcular tus impuestos) y encontrar que corre más lento de lo esperado. Luego de depurar se encuentra la causa: internamente el programa usa números racionales con miles de dígitos de precisión para representar valores que serán truncados a dos o tres dígitos al ser impresos. Esto podía ser fácilmente solucionado empezando una suma con un cero inexacto, pero esto era a menudo no intuitivo y difícil de depurar para los principiantes.

    Entonces en Python utilicé el otro modelo numérico que me era familiar, C. C tiene enteros y números de punto flotante de varios tamaños. Entonces elegí representar los enteros de Python con longs de C (garantizando por lo menos 32 bits de precisión) y los números de punto flotante con doubles de C. Luego añadí un tipo entero con precisión arbitraria que llamé "long".

    El mayor error fue que también tomé prestada una regla que tenía sentido en C pero no en un lenguaje de tan alto nivel. Para las operaciones aritméticas estándares, incluyendo la división, el resultado siempre sería del mismo tipo que los operandos. Para empeorar las cosas, inicialmente usé otra regla equivocada que prohibía usar aritmética mixta, con la idea de hacer que las implementaciones de los distintos tipos sean independientes entre sí. Entonces, en un principio no se podía sumar un int con un float, o incluso un int con un long. Luego de que Python se distribuyera públicamente, Time Peters rápidamente me convenció de que esto era realmente una mala idea e introdujo una aritmética mixta con las reglas de coerción típicas. Por ejemplo, mezclar un operando int y uno long convertiría el argumento de tipo int a long y retornaría un long como resultado y la mezcla con un float convertiría al argumento int o long en un float y retornaría un resultado float.

    Desafortunadamente, el daño estaba hecho: la división entre enteros daba un resultado entero. Estarás pensando "¿por qué era esto tan malo?". ¿Estaba haciendo escándalo por nada? Históricamente, la propuesta para cambiar esto ha tenido algunas oposiciones duras por parte de quienes pensaban que aprender división entre enteros era una de los más útiles "ritos de iniciación" para todos los programadores. Así que déjenme explicarles las razones por la cual considero esto un error de diseño.

    Cuando escribes una función para implementar un algoritmo numérico (por ejemplo, calcular las fases de la luna) esperas que los argumentos sean números de punto flotante. Sin embargo, ya que Python no tiene declaración de tipos, nada evita que la función sea llamada con argumentos enteros. En un lenguaje estáticamente tipado, como C, el compilador convertiría los argumentos a floats, pero Python no sabe nada de eso; el algoritmo corre con valores enteros hasta que las maravillas de la aritmética mixta produzca resultados intermedios que sean floats.

    Para todo excepto para la división, los enteros se comportan de la misma forma que sus números de punto flotante correspondiente. Por ejemplo, 1+1 es igual a 2 de la misma forma que 1.0+1.0 es igual a 2.0, y así. Por lo tanto uno puede fácilmente confundirse y esperar que los algoritmos numéricos funcionen independientemente de si son ejecutados con argumentos enteros o de punto flotante. Sin embargo, cuando hay una división, y existe la posibilidad de que los dos operandos sean enteros, el resultado numérico es truncado silenciosamente, esencialmente introduciendo un gran error potencial en el cómputo. Aunque uno puede escribir código defensivo que convierta todos los argumentos a float apenas se introducen, esto es tedioso, y no mejora la legibilidad o mantenibilidad del código. Adicionalmente, evita que el algoritmo sea usado con números complejos (aunque eso sería en casos muy especiales).

    Nuevamente, todo esto es un problema porque Python no convierte argumentos automáticamente a algún tipo declarado. Pasar un argumento inválido, por ejemplo un string, es generalmente atrapado rápido por que muy pocas operaciones aceptan mezclar operados strings/números (siendo la excepción la multiplicación). Sin embargo, pasar un entero puede causar una respuesta cercana a la correcta, pero con error; difícil de depurar o incluso advertir (esto me pasó recientemente en un programa que dibuja un reloj analógico; las posiciones de las manecillas eran calculadas incorrectamente debido al truncamiento, pero el error era apenas detectable excepto en ciertas horas del día).

    Arreglar la división entre enteros no fue una tarea fácil debido a los programas que dependían de este truncamiento. Se añadió al lenguaje un operador de división truncada (//) que provee la misma funcionalidad. Además, se introduzco un mecanismo ("from __future__ import division") para habilitar fácilmente la nueva semántica para la división entre enteros. Finalmente, se agregó una bandera para la línea de comando (-Qxxx) para cambiar el comportamiento y para asistir la conversión de programas. Afortunadamente, el comportamiento correcto se convirtió en el comportamiento por defecto en Python 3000.

    Traducido por Juan José Conti.
    Revisado por César Portela.
    Si encontrás errores en esta traducción, por favor reportalos en un comentario y los corregiremos a la brevedad.

    Todas las traducciones de esta serie pueden encontrarse en La historia de Python.


    Gané una de las becas del Programa Bicentenario de Investigación y Posgrado de la UTN!

    Hace casi dos meses empecé a reunir toda la información necesaria para presentarme al programa de Bicentenario de Investigación y Posgrado de la UTN. En realidad es un programa nacional, pero cada universidad lo implementaba con autonomía. La UTN daba 20 plazas en todo el país.

    De las bases:

    La Universidad Tecnológica Nacional, a través de la Secretaría de Ciencia y Tecnología y la Subsecretaría de Posgrado del Rectorado, convoca a jóvenes graduados con interés en desarrollar actividades de investigación y cursar estudios de posgrado en áreas tecnológicas prioritarias (Ver Punto 13 Áreas Temáticas de la Convocatoria) a postularse para becas con el fin de realizar carreras de maestría en la Universidad Tecnológica Nacional u otras universidades del país.

    Hoy recibí este mail (cito parte):

    Estimado Juan José Nos dirigirnos a usted a efectos de comunicarle que por Res. del Rector – Ad Referendum del Consejo Superior Nº 1067/09, se aprobó el orden de mérito para la adjudicación de las becas del Programa Bicentenario de Investigación y Posgrado. En tal sentido nos es grato informarle que usted figura en la 7º posición del orden de mérito por lo que se le ha otorgado la beca solicitada. Oportunamente, y a través de correo postal, se le informará fehacientemente sobre el particular.

    Estoy contento!


    Euler 8 (Python)

    Enunciado 8

    Encontrar el mayor producto de cinco dígitos consecutivos en este número de 100 dígitos:

    73167176531330624919225119674426574742355349194934 96983520312774506326239578318016984801869478851843 85861560789112949495459501737958331952853208805511 12540698747158523863050715693290963295227443043557 66896648950445244523161731856403098711121722383113 62229893423380308135336276614282806444486645238749 30358907296290491560440772390713810515859307960866 70172427121883998797908792274921901699720888093776 65727333001053367881220235421809751254540594752243 52584907711670556013604839586446706324415722155397 53697817977846174064955149290862569321978468622482 83972241375657056057490261407972968652414535100474 82166370484403199890008895243450658541227588666881 16427171479924442928230863465674813919123162824586 17866458359124566529476545682848912883142607690042 24219022671055626321111109370544217506941658960408 07198403850962455444362981230987879927244284909188 84580156166097919133875499200524063689912560717606 05886116467109405077541002256983155200055935729725 71636269561882670428252483600823257530420752963450

    Solución

    >>> b = 7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450
    
    >>> s = str(b)
    
    >>> s
    
    '7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450'
    
    >>> l = list(s)
    
    >>> l
    
    ['7', '3', '1', '6', '7', '1', '7', '6', '5', '3', '1', '3', '3', '0', '6', '2', '4', '9', '1', '9', '2', '2', '5', '1', '1', '9', '6', '7', '4', '4', '2', '6', '5', '7', '4', '7', '4', '2', '3', '5', '5', '3', '4', '9', '1', '9', '4', '9', '3', '4', '9', '6', '9', '8', '3', '5', '2', '0', '3', '1', '2', '7', '7', '4', '5', '0', '6', '3', '2', '6', '2', '3', '9', '5', '7', '8', '3', '1', '8', '0', '1', '6', '9', '8', '4', '8', '0', '1', '8', '6', '9', '4', '7', '8', '8', '5', '1', '8', '4', '3', '8', '5', '8', '6', '1', '5', '6', '0', '7', '8', '9', '1', '1', '2', '9', '4', '9', '4', '9', '5', '4', '5', '9', '5', '0', '1', '7', '3', '7', '9', '5', '8', '3', '3', '1', '9', '5', '2', '8', '5', '3', '2', '0', '8', '8', '0', '5', '5', '1', '1', '1', '2', '5', '4', '0', '6', '9', '8', '7', '4', '7', '1', '5', '8', '5', '2', '3', '8', '6', '3', '0', '5', '0', '7', '1', '5', '6', '9', '3', '2', '9', '0', '9', '6', '3', '2', '9', '5', '2', '2', '7', '4', '4', '3', '0', '4', '3', '5', '5', '7', '6', '6', '8', '9', '6', '6', '4', '8', '9', '5', '0', '4', '4', '5', '2', '4', '4', '5', '2', '3', '1', '6', '1', '7', '3', '1', '8', '5', '6', '4', '0', '3', '0', '9', '8', '7', '1', '1', '1', '2', '1', '7', '2', '2', '3', '8', '3', '1', '1', '3', '6', '2', '2', '2', '9', '8', '9', '3', '4', '2', '3', '3', '8', '0', '3', '0', '8', '1', '3', '5', '3', '3', '6', '2', '7', '6', '6', '1', '4', '2', '8', '2', '8', '0', '6', '4', '4', '4', '4', '8', '6', '6', '4', '5', '2', '3', '8', '7', '4', '9', '3', '0', '3', '5', '8', '9', '0', '7', '2', '9', '6', '2', '9', '0', '4', '9', '1', '5', '6', '0', '4', '4', '0', '7', '7', '2', '3', '9', '0', '7', '1', '3', '8', '1', '0', '5', '1', '5', '8', '5', '9', '3', '0', '7', '9', '6', '0', '8', '6', '6', '7', '0', '1', '7', '2', '4', '2', '7', '1', '2', '1', '8', '8', '3', '9', '9', '8', '7', '9', '7', '9', '0', '8', '7', '9', '2', '2', '7', '4', '9', '2', '1', '9', '0', '1', '6', '9', '9', '7', '2', '0', '8', '8', '8', '0', '9', '3', '7', '7', '6', '6', '5', '7', '2', '7', '3', '3', '3', '0', '0', '1', '0', '5', '3', '3', '6', '7', '8', '8', '1', '2', '2', '0', '2', '3', '5', '4', '2', '1', '8', '0', '9', '7', '5', '1', '2', '5', '4', '5', '4', '0', '5', '9', '4', '7', '5', '2', '2', '4', '3', '5', '2', '5', '8', '4', '9', '0', '7', '7', '1', '1', '6', '7', '0', '5', '5', '6', '0', '1', '3', '6', '0', '4', '8', '3', '9', '5', '8', '6', '4', '4', '6', '7', '0', '6', '3', '2', '4', '4', '1', '5', '7', '2', '2', '1', '5', '5', '3', '9', '7', '5', '3', '6', '9', '7', '8', '1', '7', '9', '7', '7', '8', '4', '6', '1', '7', '4', '0', '6', '4', '9', '5', '5', '1', '4', '9', '2', '9', '0', '8', '6', '2', '5', '6', '9', '3', '2', '1', '9', '7', '8', '4', '6', '8', '6', '2', '2', '4', '8', '2', '8', '3', '9', '7', '2', '2', '4', '1', '3', '7', '5', '6', '5', '7', '0', '5', '6', '0', '5', '7', '4', '9', '0', '2', '6', '1', '4', '0', '7', '9', '7', '2', '9', '6', '8', '6', '5', '2', '4', '1', '4', '5', '3', '5', '1', '0', '0', '4', '7', '4', '8', '2', '1', '6', '6', '3', '7', '0', '4', '8', '4', '4', '0', '3', '1', '9', '9', '8', '9', '0', '0', '0', '8', '8', '9', '5', '2', '4', '3', '4', '5', '0', '6', '5', '8', '5', '4', '1', '2', '2', '7', '5', '8', '8', '6', '6', '6', '8', '8', '1', '1', '6', '4', '2', '7', '1', '7', '1', '4', '7', '9', '9', '2', '4', '4', '4', '2', '9', '2', '8', '2', '3', '0', '8', '6', '3', '4', '6', '5', '6', '7', '4', '8', '1', '3', '9', '1', '9', '1', '2', '3', '1', '6', '2', '8', '2', '4', '5', '8', '6', '1', '7', '8', '6', '6', '4', '5', '8', '3', '5', '9', '1', '2', '4', '5', '6', '6', '5', '2', '9', '4', '7', '6', '5', '4', '5', '6', '8', '2', '8', '4', '8', '9', '1', '2', '8', '8', '3', '1', '4', '2', '6', '0', '7', '6', '9', '0', '0', '4', '2', '2', '4', '2', '1', '9', '0', '2', '2', '6', '7', '1', '0', '5', '5', '6', '2', '6', '3', '2', '1', '1', '1', '1', '1', '0', '9', '3', '7', '0', '5', '4', '4', '2', '1', '7', '5', '0', '6', '9', '4', '1', '6', '5', '8', '9', '6', '0', '4', '0', '8', '0', '7', '1', '9', '8', '4', '0', '3', '8', '5', '0', '9', '6', '2', '4', '5', '5', '4', '4', '4', '3', '6', '2', '9', '8', '1', '2', '3', '0', '9', '8', '7', '8', '7', '9', '9', '2', '7', '2', '4', '4', '2', '8', '4', '9', '0', '9', '1', '8', '8', '8', '4', '5', '8', '0', '1', '5', '6', '1', '6', '6', '0', '9', '7', '9', '1', '9', '1', '3', '3', '8', '7', '5', '4', '9', '9', '2', '0', '0', '5', '2', '4', '0', '6', '3', '6', '8', '9', '9', '1', '2', '5', '6', '0', '7', '1', '7', '6', '0', '6', '0', '5', '8', '8', '6', '1', '1', '6', '4', '6', '7', '1', '0', '9', '4', '0', '5', '0', '7', '7', '5', '4', '1', '0', '0', '2', '2', '5', '6', '9', '8', '3', '1', '5', '5', '2', '0', '0', '0', '5', '5', '9', '3', '5', '7', '2', '9', '7', '2', '5', '7', '1', '6', '3', '6', '2', '6', '9', '5', '6', '1', '8', '8', '2', '6', '7', '0', '4', '2', '8', '2', '5', '2', '4', '8', '3', '6', '0', '0', '8', '2', '3', '2', '5', '7', '5', '3', '0', '4', '2', '0', '7', '5', '2', '9', '6', '3', '4', '5', '0']
    
    >>> i = [int(x) for x in l]
    
    >>> i
    
    [7, 3, 1, 6, 7, 1, 7, 6, 5, 3, 1, 3, 3, 0, 6, 2, 4, 9, 1, 9, 2, 2, 5, 1, 1, 9, 6, 7, 4, 4, 2, 6, 5, 7, 4, 7, 4, 2, 3, 5, 5, 3, 4, 9, 1, 9, 4, 9, 3, 4, 9, 6, 9, 8, 3, 5, 2, 0, 3, 1, 2, 7, 7, 4, 5, 0, 6, 3, 2, 6, 2, 3, 9, 5, 7, 8, 3, 1, 8, 0, 1, 6, 9, 8, 4, 8, 0, 1, 8, 6, 9, 4, 7, 8, 8, 5, 1, 8, 4, 3, 8, 5, 8, 6, 1, 5, 6, 0, 7, 8, 9, 1, 1, 2, 9, 4, 9, 4, 9, 5, 4, 5, 9, 5, 0, 1, 7, 3, 7, 9, 5, 8, 3, 3, 1, 9, 5, 2, 8, 5, 3, 2, 0, 8, 8, 0, 5, 5, 1, 1, 1, 2, 5, 4, 0, 6, 9, 8, 7, 4, 7, 1, 5, 8, 5, 2, 3, 8, 6, 3, 0, 5, 0, 7, 1, 5, 6, 9, 3, 2, 9, 0, 9, 6, 3, 2, 9, 5, 2, 2, 7, 4, 4, 3, 0, 4, 3, 5, 5, 7, 6, 6, 8, 9, 6, 6, 4, 8, 9, 5, 0, 4, 4, 5, 2, 4, 4, 5, 2, 3, 1, 6, 1, 7, 3, 1, 8, 5, 6, 4, 0, 3, 0, 9, 8, 7, 1, 1, 1, 2, 1, 7, 2, 2, 3, 8, 3, 1, 1, 3, 6, 2, 2, 2, 9, 8, 9, 3, 4, 2, 3, 3, 8, 0, 3, 0, 8, 1, 3, 5, 3, 3, 6, 2, 7, 6, 6, 1, 4, 2, 8, 2, 8, 0, 6, 4, 4, 4, 4, 8, 6, 6, 4, 5, 2, 3, 8, 7, 4, 9, 3, 0, 3, 5, 8, 9, 0, 7, 2, 9, 6, 2, 9, 0, 4, 9, 1, 5, 6, 0, 4, 4, 0, 7, 7, 2, 3, 9, 0, 7, 1, 3, 8, 1, 0, 5, 1, 5, 8, 5, 9, 3, 0, 7, 9, 6, 0, 8, 6, 6, 7, 0, 1, 7, 2, 4, 2, 7, 1, 2, 1, 8, 8, 3, 9, 9, 8, 7, 9, 7, 9, 0, 8, 7, 9, 2, 2, 7, 4, 9, 2, 1, 9, 0, 1, 6, 9, 9, 7, 2, 0, 8, 8, 8, 0, 9, 3, 7, 7, 6, 6, 5, 7, 2, 7, 3, 3, 3, 0, 0, 1, 0, 5, 3, 3, 6, 7, 8, 8, 1, 2, 2, 0, 2, 3, 5, 4, 2, 1, 8, 0, 9, 7, 5, 1, 2, 5, 4, 5, 4, 0, 5, 9, 4, 7, 5, 2, 2, 4, 3, 5, 2, 5, 8, 4, 9, 0, 7, 7, 1, 1, 6, 7, 0, 5, 5, 6, 0, 1, 3, 6, 0, 4, 8, 3, 9, 5, 8, 6, 4, 4, 6, 7, 0, 6, 3, 2, 4, 4, 1, 5, 7, 2, 2, 1, 5, 5, 3, 9, 7, 5, 3, 6, 9, 7, 8, 1, 7, 9, 7, 7, 8, 4, 6, 1, 7, 4, 0, 6, 4, 9, 5, 5, 1, 4, 9, 2, 9, 0, 8, 6, 2, 5, 6, 9, 3, 2, 1, 9, 7, 8, 4, 6, 8, 6, 2, 2, 4, 8, 2, 8, 3, 9, 7, 2, 2, 4, 1, 3, 7, 5, 6, 5, 7, 0, 5, 6, 0, 5, 7, 4, 9, 0, 2, 6, 1, 4, 0, 7, 9, 7, 2, 9, 6, 8, 6, 5, 2, 4, 1, 4, 5, 3, 5, 1, 0, 0, 4, 7, 4, 8, 2, 1, 6, 6, 3, 7, 0, 4, 8, 4, 4, 0, 3, 1, 9, 9, 8, 9, 0, 0, 0, 8, 8, 9, 5, 2, 4, 3, 4, 5, 0, 6, 5, 8, 5, 4, 1, 2, 2, 7, 5, 8, 8, 6, 6, 6, 8, 8, 1, 1, 6, 4, 2, 7, 1, 7, 1, 4, 7, 9, 9, 2, 4, 4, 4, 2, 9, 2, 8, 2, 3, 0, 8, 6, 3, 4, 6, 5, 6, 7, 4, 8, 1, 3, 9, 1, 9, 1, 2, 3, 1, 6, 2, 8, 2, 4, 5, 8, 6, 1, 7, 8, 6, 6, 4, 5, 8, 3, 5, 9, 1, 2, 4, 5, 6, 6, 5, 2, 9, 4, 7, 6, 5, 4, 5, 6, 8, 2, 8, 4, 8, 9, 1, 2, 8, 8, 3, 1, 4, 2, 6, 0, 7, 6, 9, 0, 0, 4, 2, 2, 4, 2, 1, 9, 0, 2, 2, 6, 7, 1, 0, 5, 5, 6, 2, 6, 3, 2, 1, 1, 1, 1, 1, 0, 9, 3, 7, 0, 5, 4, 4, 2, 1, 7, 5, 0, 6, 9, 4, 1, 6, 5, 8, 9, 6, 0, 4, 0, 8, 0, 7, 1, 9, 8, 4, 0, 3, 8, 5, 0, 9, 6, 2, 4, 5, 5, 4, 4, 4, 3, 6, 2, 9, 8, 1, 2, 3, 0, 9, 8, 7, 8, 7, 9, 9, 2, 7, 2, 4, 4, 2, 8, 4, 9, 0, 9, 1, 8, 8, 8, 4, 5, 8, 0, 1, 5, 6, 1, 6, 6, 0, 9, 7, 9, 1, 9, 1, 3, 3, 8, 7, 5, 4, 9, 9, 2, 0, 0, 5, 2, 4, 0, 6, 3, 6, 8, 9, 9, 1, 2, 5, 6, 0, 7, 1, 7, 6, 0, 6, 0, 5, 8, 8, 6, 1, 1, 6, 4, 6, 7, 1, 0, 9, 4, 0, 5, 0, 7, 7, 5, 4, 1, 0, 0, 2, 2, 5, 6, 9, 8, 3, 1, 5, 5, 2, 0, 0, 0, 5, 5, 9, 3, 5, 7, 2, 9, 7, 2, 5, 7, 1, 6, 3, 6, 2, 6, 9, 5, 6, 1, 8, 8, 2, 6, 7, 0, 4, 2, 8, 2, 5, 2, 4, 8, 3, 6, 0, 0, 8, 2, 3, 2, 5, 7, 5, 3, 0, 4, 2, 0, 7, 5, 2, 9, 6, 3, 4, 5, 0]
    
    >>> def mult(lista):
    
    ...     return reduce(mul, lista, 1)
    
    ...
    
    >>> mult([])
    
    1
    
    >>> mult([1,2,3])
    
    6
    
    >>> for x in range(1001-5):
    
    ...     m = mult(i[x:x+5])
    
    ...     if m > maxmult:
    
    ...             maxmult = m
    
    ...
    
    >>> maxmult
    
    40824


    Sorry por el SPAM

    Desde el viernes a la noche, cuando alguien recibía actualizaciones de mi blog vía Google Reader, obtenía un pedazo de SPAM en lugar de mis textos:

    Estoy trabajando para solucionarlo.


    Fito en el Municipal

    Ayer a la mañana me desperté sin saber que por la noche iba a ver por primera vez un recital de Fito Páez. ¿24 años de vida y nunca lo había visto? No. Y no es que no me guste; me gusta mucho. Pero no había tenido la oportunidad. Hacía varias semanas que publicitaban el show en el Teatro Municipal, pero cuando quise comprar entradas, ya estaban agotadas :(

    Resignado a perdérmelo, el jueves por la tarde casi no me acordaba del evento, cuando un mensaje de un compañero de la facu llegó a mi celular. Le sobraba una entrada y me la ofrecía. Sin dudarlo de dije que si, nos vemos 8, 8:30 en la puerta ya que las que tenemos no son numeradas.

    Un rato antes de las 8 manejé hasta el centro, cuando doblé en Juan de Garay, la calle al costado del teatro, todos los lugares en los que podía estacionar estaban ocupados. Seguí de largo, vuelta a la manza, otra vuelta. No puede ser. 8:20. Me alejo unas cuadras más y veo un lugar entre un remís y una camioneta. Todavía no estacioné nunca en la ciudad en un espacio tan chico. 8:25. Hago el intento; balizas, para atrás, para adelante, para trás. Me bajo, cierro, alarma, lo miro. Mmmmmmm no me convence, la cola sale un poquito para afuera. Me vuelvo a meter en el auto, unas maniobras, me vuelvo a bajar. Mmmmmmm no me convence, repito el procedimiento. Me vuelvo a bajar, lo miro, igual que al principio. 8:32.

    Teléfono, Martín (de la facu). Estamos en la puerta por entrar, apurate. Ya estoy. Empiezo a correr, media cuadra, me freno. ¿Puse la alarma? Me vuelvo. Si, la puse. Vuelvo a correr, llego a la esquina y me doy cuenta que estoy a 3 cuadras del teatro. No estoy en forma, sigo corriendo. Llego, voy por la entrada principal, solo plateas, doy la vuelta. Lo encuentro a Martín y agitadísmo entro a subir escaleras hasta que llegamos a nuestros lugares, arriba de todo pero bien al medio: Me encantó la relación precio/beneficio de nuestros asientos.

    De cuando empezó el recital en adelante no puedo contar mucho, solo que fue impresionante cuando se apagaron las luces, entro Rodolfo Páez y desde arriba vi cientos de cuadraditos de luz (los displays de cámaras y celulares) y flashes que como relámpagos iluminaban el escenario.

    [gallery]

    Un post distinto a los que suelo hacer, y con unas descripciones a veces forzando la realidad para hacerlo más cómico :)

    La mayoría de las fotos son del blog Welcome. Gracias.


    La historia de Python: Cómo las excepciones llegaron a ser clases

    El siguiente texto es una traducción del artículo How Exceptions Came to be Classes de Guido van Rossum publicado en http://python-history.blogspot.com/.

    Cómo las excepciones llegaron a ser clases

    Pronto supe que quería que Python utilice excepciones para el manejo de errores. Sin embargo, una parte crítica para que las excepciones funcionen es lograr algún tipo de esquema para identificar distintos tipos de excepciones. En los lenguajes modernos (incluyendo el moderno Python :-), las excepciones son definidas en términos de clases definidas por los usuarios. Sin embargo, en los comienzos de Python, elegí identificar las excepciones por strings. Esto fue desafortunado, pero tenía dos razones para tomar esta aproximación. Primero, aprendí sobre las excepciones en Modula-3, donde las mismas son señales únicas. Segundo, introduje las excepciones antes de introducir las clases definidas por los usuarios.

    En teoría, supongo que podría haber creado un nuevo tipo de objeto a medida para ser utilizado en excepciones, pero como todo tipo de objeto a medida requiere un considerable esfuerzo de codificación en C, decidí reutilizar un tipo a medida existente. Y, ya que las excepciones están relacionadas con mensajes de error, pareció natural usar strings para representar excepciones.

    Lamentablemente elegí una semántica donde diferentes objetos string podían representar diferentes excepciones, aún cuando tenían el mismo valor (es decir, contenían la misma secuencia de caracteres). Elegí esta semántica porque quería que las excepciones definidas en distintos módulos sean independientes, incluso si sucedía que tenían el mismo valor. La idea era que las excepciones siempre debían ser referenciadas por su nombre, lo que implicaría identidad del objeto, nunca por su valor, lo que requeriría string iguales.

    Este enfoque fue influenciado por las excepciones de Modula-3, donde cada declaración de excepción crea una única “señal de excepción” que no puede ser confundida con cualquier otra. Creo que también quería optimizar el testeo de excepciones usando punteros de comparación en vez de valores de strings en un equivocado intento de optimización prematura del tiempo de ejecución (un caso inusual – ¡yo generalmente optimizaba mi propio tiempo de codificación!). La principal razón, sin embargo, es que me preocupaban los conflictos de nombres entre excepciones no relacionadas definidas en diferentes módulos. Intenté usar patrones para adherirme estrictamente a la convención de definir una excepción como una constante global en algunos módulos, y, entonces, usarla por nombre en todo, lanzamiento o captura (esto fue mucho tiempo antes de que ciertos string literales fueran automáticamente “internos”).

    ¡Ay de mí!, en la práctica las cosas nunca resultan como se espera. Pronto los usuarios de Python descubrieron que dentro del mismo módulo, el compilador byte code unificaba los strings literales (es decir, creaba un sólo objeto compartido para todas las ocurrencias de strings literales con el mismo valor). Así, por accidente, los usuarios encontraron que las excepciones podían ser capturadas especificando el nombre de la excepción o el string literal conteniendo el mensaje de error. Bien, al menos esto parecía trabajar la mayor parte del tiempo. En realidad, esto sólo trabajó para el código definido en el mismo módulo. Si uno intentaba capturar excepciones usando el mensaje de error de la excepción en un módulo diferente, el código se rompía misteriosamente. Es innecesario decir que este es el tipo de cosas que causa una gran confusión.

    En 1997, con Python 1.5, introduje clases para excepciones dentro del lenguaje. Aunque este ha sido el enfoque recomendado desde entonces, las excepciones de strings todavía eran soportadas para el uso en determinadas aplicaciones heredadas hasta Python 2.5. Estas fueron eliminadas en Pyton 2.6.

    Traducido por César Portela.

    Revisado por Juan José Conti.

    Si encontrás errores en esta traducción, por favor reportalos en un comentario y los corregiremos a la brevedad.

    Todas las traducciones de esta serie pueden encontrarse en La historia de Python.