1. Una breve línea temporal de Python

    El desarrollo de Python ocurrió más o menos paralelo al de otros lenguajes dinámicos y de código abierto, como Tcl, Perl y más tarde Ruby, que estaban también desarrollándose y ganando popularidad. Para poner el nacimiento y evolución de Python en perspectiva, usaremos la lista siguiente, que muestra las diferentes versiones que se han liberado. Las fechas más antiguas son meramente estimativas, ya que en esa época no llevaba un registro detallado.

    Fecha de liberación Versión
    Diciembre de 1989 Empieza la implementación
    1990 Liberaciones internas en el CWI
    20 de febrero de 1991 0.9.0 (Liberado en alt.sources)
    febrero de 1991 0.9.1
    Otoño de 1991 0.9.2
    24 de diciembre de 1991 0.9.4
    2 de enero de 1992 0.9.5 (Sólo Macintosh)
    6 de abril de 1992 0.9.6
    En algún momento de 1992 0.9.7 beta
    9 de enero de 1993 0.9.8
    29 de julio de 1993 0.9.9
    26 de enero de 1994 1.0.0
    15 de febrero de 1994 1.0.2
    4 de mayo de 1994 1.0.3
    14 de julio de 1994 1.0.4
    11 de octubre de 1994 1.1
    10 de noviembre de 1994 1.1.1
    13 de abril de 1995 1.2
    13 de octubre de 1995 1.3
    25 de octubre de 1996 1.4
    3 de enero de 1998 1.5
    31 de octubre de 1998 1.5.1
    13 de abril de 1999 1.5.2
    5 de septiembre de 2000 1.6
    16 de octubre de 2000 2.0
    17 de abril de 2001 2.1
    21 de diciembre de 2001 2.2
    29 de julio de 2003 2.3
    30 de noviembre de 2004 2.4
    16 de septiembre de 2006 2.5
    1 de octubre de 2008 2.6
    3 de diciembre de 2008 3.0

    He añadido enlaces a las versiones que todavía están disponibles en python.org a fecha de hoy. Hay que tener en cuenta que en muchos casos las versiones principales se vieron continuados con micro-versiones, como por ejemplo la 2.0.1; no las he incluido en la tabla para mantenerla de un tamaño manejable. Existen la posibilidad de descargar el código fuente de las versiones más antiguas en la dirección http://www.python.org/ftp/python/src/. También se pueden obtener ejecutables binarios y otras curiosidades históricas descendiendo a partir de ese nivel.

    0

    Añadir un comentario

  2. En próximas entradas bucearemos en profundidad en los detalles de la historia de Python. Pero antes de eso, me gustaría desarrollar las guías filosóficas que me ayudaron a la hora de tomar decisiones mientras diseñaba y desarrollaba Python.

    En primer lugar, Python se concibió originalmente como un proyecto personal. No contaba con ningún presupuesto oficial, y quería obtener resultados pronto, en parte para convencer a la gerencia de que dieran su apoyo al proyecto (Algo que conseguí con bastante éxito). Esto condujo a una lista de reglas cuyo objetivo era no perder el tiempo:

    • Tomar prestadas ideas de otros sitios, siempre y cuando tuvieran sentido.
    • "Las cosas deberían ser lo más sencillas posibles, pero no más." (Einstein).
    • Haz una sola cosa, y hazla bien (La filosofía Unix)
    • No te preocupes demasiado por el rendimiento, planea optimizar más tarde, cuando sea necesario
    • No luches contra el entorno, fluye con él.
    • No busques la perfección, porque a menudo algo "suficientemente bueno" es eso mismo
    • (Por lo tanto) esta bien hacer algunos atajos a veces, especialmente si se puede hacer bien más tarde.

    Otros principios no fueron incluidos para ahorrar tiempo. A veces consiguieron exactamente lo contrario.

    • La implementación de Python no debería estar ligada a una plataforma en particular. Es correcto el que alguna funcionalidad no está disponible siempre, pero el núcleo debería funcionar en todas partes.
    • No molestar al usuario con detalles que la máquina puede manejar (No siempre seguí esta regla, y describiré en secciones posteriores algunas de las monstruosas consecuencias que se derivaron).
    • Dar soporte y apoyar el código de usuario independiente de la plataforma, pero no restringir el acceso a las propiedades o capacidades de la máquina (Este es una clara diferencia con Java)
    • Un sistema muy complejo debería tener múltiples niveles de extensibilidad. Esto maximiza las oportunidades del usuario para ayudarse a si mismo, ya sea este sofisticado o no,
    • Los errores no deberías ser fatales. Es decir, el código del usuario debería ser capaz de recuperarse de una condición de error en tanto en cuanto la máquina virtual esté operativa.
    • Al mismo tiempo, los errores no deberían ocurrir silenciosamente (la combinación de estos dos puntos condujo de forma natural al uso de excepciones en la implementación).
    • Un error en el código del usuario no debe ser capaz de provocar un comportamiento indeterminado en el interprete de Python; un volcado de memoria nunca debería ocurrir por una equivocación de un usuario.

    Por último, tenía muchas ideas acerca del diseño de buenos lenguajes de programación, producto de una larga exposición al grupo ABC, donde tuve mis primeras experiencias reales con el diseño e implementación de lenguajes, Estas ideas son las más difíciles de expresar con palabras, ya que tratan en su mayoría de conceptos subjetivos como elegancia, simplicidad o legibilidad.

    Aunque hablaré más acerca de la influencia de ABC sobre Python un poco más tarde, quiero mencionar aquí una regla especial sobre legibilidad: Los signos de puntuación deberían ser usados con muchas reservas, de igual manera que se usan en inglés o en álgebra. Existen excepciones cuando alguna notación en particular tiene una larga tradición de uso en otros lenguajes, como "x*y" para indicar una multiplicación, "a[i]" para acceder al contenido de un array o vector, o "x.foo" para selección de atributos, pero Python no usa un "$" para indicar variables, ni "!" para indicar operaciones con efectos colaterales.

    Tim Peters, que ha sido durante mucho tiempo usuario de Python y se ha convertido en el desarrollador del núcleo mas prolífico y tenaz, hizo un intento de capturar estos principios de diseño esquivos en lo que él llama "Python Zen", que cito integro a continuación:

    • Bello es mejor que feo.
    • Explicito es mejor que implícito.
    • Simple es mejor que complejo.
    • Complejo es mejor que complicado.
    • Plano es mejor que anidado.
    • Disperso es mejor que denso.
    • La legibilidad cuenta
    • Los casos especiales no son tan especiales como para romper las reglas.
    • Aunque lo práctico vence a lo puro.
    • Los errores nunca deberían ocurrir silenciosamente.
    • A menos que se hayan silenciado explícitamente
    • Enfrentado a la ambigüedad, rechaza la tentación de suponer.
    • Siempre debería haber una forma obvia (Y preferiblemente solo una forma) de hacer las cosas
    • Aunque esa forma puede no ser obvia a primera vista, a no ser que seas Holandés.
    • Ahora es mejor que nunca.
    • Aunque nunca es a veces preferible a ahora mismo.
    • Si la implementación es difícil de explicar, es una mala idea.
    • Si la implementación es fácil de explicar, puede que sea una buena idea.
    • Los espacios de nombres son una idea fantástica, ¡Hay que usarlos más!

    Aunque mi experiencia con ABC influyó en gran medida sobre Python, hay ciertos principios de diseño aceptados por el grupo ABC que eran radicalmente diferentes de los tomados en Python. De muchas maneras, Python es una separación consciente de lo siguiente:

    • El grupo ABC aspiraba a la perfección. Por ejemplo, usaban algoritmos y estructuras de datos basadas en árboles, que se había demostrado que eran óptimas para grandes colecciones asintóticas, pero que no eran tan eficientes en colecciones pequeñas.
    • El grupo ABC quería aislar al usuario, de la forma más completa posible, de todo esos "grandes y malvados ordenadores". No solo era que no debiera existir límite alguno en el rango de números, la longitud de las cadenas o el tamaño de las colecciones (Más allá de la memoria total disponible), es que a los usuarios no se les dejaba interactuar con archivos, discos, la operación de "salvar" o interactuar con otros programas. ABC debía ser la única herramienta que necesitara el usuario. Estas aspiraciones provocaron que el grupo ABC creara un sofisticado sistema de edición integrado, específico de ABC (Existía una posible vía de escape del entorno de ABC, cierto, pero era en su mayoría un apaño hecho a posteriori, y no directamente accesible desde el lenguaje).
    • El grupo ABC asumió que los usuarios no tenían experiencia previa con los ordenadores (O se les forzó a olvidarla). Por esa razón se introdujo una terminología alternativa más "amigable" con el novato, en vez de usar los términos habituales. Así, por ejemplo, los procedimientos eran llamados "how-tos" (Receta) y las variables, "locations" (Localidades).
    • El grupo ABC diseño el lenguaje ABC sin tener una vía evolutiva en mente, y sin esperar que el usuario participara en su diseño. ABC se creo como un sistema cerrado, tan libre de fallos como sus desarrolladores pudieran conseguir, A los usuarios no se les animaba a mirar "debajo del capó". Aunque se habló de abrir partes de la implementación para usuarios avanzados en etapas posteriores del proyecto, nunca se llegó a realizar.

    La filosofía de diseño que usé en la creación de Python es probablemente, y de muy variadas formas, una de las principales razones de su éxito. En vez de aspirar a la perfección, los primeros que lo probaron se encontraron con que Python funcionaba "lo suficientemente bien" para sus propósitos. A medida que crecía la base de usuarios se fueron incorporando sugerencias y mejoras. Como veremos en secciones posteriores, muchas de esas mejoras implicaban cambios sustanciales y reescritura de partes del núcleo del lenguaje. Incluso a día de hoy, Python continua evolucionando.

    1

    Ver comentarios

  3. Introducción y visión general

    Python es actualmente uno los los lenguajes de programación dinámicos más populares, junto con Perl, Tcl, PHP y el recién llegado Ruby. Aunque es descrito a menudo como un lenguaje de "scripting", en realidad es un lenguaje de programación completo y de uso general, en la misma línea que Lisp o Smalltalk (Al igual que los anteriormente nombrados, por cierto). Hoy, Python se usa para todo, desde guiones ligeros de usar y tirar hasta servidores web de alta escalabilidad, que proporcionan servicios ininterrumpidos de tipo 24x7. Se usa para programas visuales, para acceso a bases de datos, para programación web tanto en el lado cliente como en el servidor, y para la ejecución de baterías de pruebas de aplicaciones. Lo usan tanto científicos escribiendo aplicaciones para superordenadores como niños que están aprendiendo a programar.

    En este blog veremos las partes más destacadas de la historia de Python, especialmente la forma en que fue desarrollado, las principales influencias en su diseño, los errores cometidos, las lecciones aprendidas y la dirección futura del lenguaje.

    Agradecimientos: Estoy en deuda con Dave Beazley por muchas de las mejores frases de este blog (Para saber más de los orígenes del blog, véase el blog principal de Guido Van Rossum)

    Un vistazo general a Python

    La primera vez que uno se encuentra con Python, lo primero que notará es que el código parece, al menos a primera vista, muy similar al código escrito en otros lenguajes de programación convencionales, como pueden ser C o Pascal. Esto no es una casualidad; la sintaxis de Python toma prestadas muchísimas cosas de C. La mayoría de las palabras reservadas de Python (if, else, while, for, etc...) son las mismas que en C, se siguen las mismas reglas para formar identificadores válidos en Python que en C, y la mayoría de los operadores estándar tienen el mismo significado en los dos lenguajes. Por supuesto, resulta obvio que Python no es C, y una de las diferencias que más llama la atención es el uso de la indentación, en vez de los corchetes, para agrupar sentencias. Por ejemplo, en vez de escribir un código en C como el siguiente:

        if (a < b) {
            max = b;
        } else {
            max = a;
        }
    

    Python simplemente se deshace de los corchetes (junto con los puntos y comas finales) y utiliza la siguiente estructura:

        if a < b:
            max = b
        else:
            max = a
    

    El otro aspecto relevante en que Python difiere de otros lenguajes similares a C es en el uso de tipado dinámico. En C, las variables siempre deben ser declaradas explícitamente, y se les debe asignar un tipo, como int o double. Esta información se usa luego para realizar comprobaciones estáticas durante la fase de compilación, así como para reservar el uso de la memoria necesaria para almacenar el valor que almacenará la variable. En Python, los nombres de variables son sólo nombres que referencian objetos. Las variables no necesitan ser declaradas antes de ser asignadas, y pueden incluso cambiar de tipo durante la ejecución de un programa. Al igual que otros lenguajes dinámicos, toda la comprobación de tipos se realiza en tiempo de ejecución por un intérprete en vez de ser una parte del proceso de compilación.

    Los tipos de datos primitivos de Python incluyen valores lógicos o booleanos, números (enteros a nivel máquina, enteros de precisión arbitraria y tipos reales y complejos con valores en coma flotante) y cadenas de texto o strings (tanto de 8 bits como unicode). Todos estos son tipos inmutables, lo que significa que sus valores se representan con objetos que no pueden ser modificados después de su creación. Los tipos de datos compuestos incluyen tuplas (arrays o vectores inmutables), listas (arrays o vectores modificables en tamaño) y diccionarios (tablas hash). Para organizar los programas, Python soporta paquetes (packages), que son grupos de módulos y/o paquetes), módulos (código relacionado que se agrupa junto en un mismo archivo fuente), clases, métodos y funciones. Para control de flujo, proporciona las construcciones if/else, while y una sentencia for de alto nivel que realiza un bucle sobre cualquier objeto que sea "iterable". Para el manejo de errores, Python utiliza excepciones (no continuables). Una sentencia raise eleva una excepción, y las sentencias try/except/finally sirven para especificar manejadores de excepciones. Las operaciones y las funciones incorporadas elevan excepciones cuando se encuentran con una condición de error.

    En Python, todos los objetos que pueden ser nominados se dice que son objetos de "primera clase". Esto significa que cualquier función, clase, método, módulo y cualquier otra cosa que tenga nombre pueden ser pasados como parámetros, inspeccionados y colocados en diferentes estructuras de datos. (por ejemplo, listas o diccionarios) en tiempo de ejecución. Y hablando de objetos, Python también tiene soporte completo para la programación orientada a objetos, incluyendo clases definidas por el usuario, herencia y polimorfismo, basado en la vinculación en tiempo de ejecución de los métodos.

    Python dispone de una librería estándar bastante grande, una de las principales razones de su popularidad. La librería estándar consta de más de 100 módulos, y está siempre en continua evolución. Entre otras cosas, hay módulos para usar expresiones regulares, funciones matemáticas habituales, multiproceso ligero o threads, interfaces con diferentes sistemas operativos, redes, protocolos estándar de Internet (HTTP, FTP, SMTP, entre otros), manejo de correo electrónico, procesamiento de XML, analizado de HTML e interfaces gráficas (Tcl/Tk).

    Además, hay una enorme cantidad de módulos y paquetes suministrados por terceras partes, la mayoría de los cuales son software libre. Aquí podemos encontrarnos con frameworks para la web (¡Demasiados para listarlos todos!), más sistemas de programación gráficas, librerías de cálculo numérico eficientes (incluyendo recubrimientos de algunos paquetes de Fortran muy populares), interfaces con bases de datos relacionales (Como Oracle y MySql, entre otras), SWIG (Una herramienta que permite realizar cualquier tipo de llamadas a librerías escritas en C/C++, que se verían como módulos Python) y mucho más.

    Un atractivo importante que tiene Python (así como otros lenguajes de programación dinámicos, por cierto) es que tareas en principio aparentemente complicadas pueden ser expresadas con muy poco código. Como ejemplo, veremos un sencillo guión de Python que descarga una página web, inspecciona el código HTML de la misma buscando referencias URL, e imprime las diez primeras que encuentre:

        # Scan the web looking for references
    
        import re
        import urllib
    
        regex = re.compile(r'href="([^"]+)"')
    
        def matcher(url, max=10):
            "Print the first several URL references in a given url."
            data = urllib.urlopen(url).read()
            hits = regex.findall(data)
            for hit in hits[:max]:
                print urllib.basejoin(url, hit)
    
        matcher("http://python.org")
    

    Este programa puede ser modificado fácilmente para implementar una "araña" web, de hecho Scott Hassan me ha dicho que la primera araña de Google fue escrita en Python. A día de hoy, Google emplea millones de líneas de código Python para gestionar muchos aspectos de sus operaciones, desde compilación automatizada hasta la gestión de anuncios (Nota: Guido Van Rossom es actualmente un empleado de Google).

    Si miramos en su interior, veríamos que Python está implementado usando una combinación de compilador a bytecode e interprete. La compilación se realiza de manera implícita cada vez que se importa un módulo, y hay varias primitivas del lenguaje que requieren que el compilador esté disponible en tiempo de ejecución. Aunque la implementación estándar de Python está escrita en C, y por tanto disponible para casi cualquier combinación de hardware/software, existen otras implementaciones que han llegado a ser populares. Jython es una versión que corre sobre la Maquina Virtual Java y tiene una integración magnifica con Java. IronPython es una versión para la plataforma .NET de Microsoft, que se integra de manera similar con los lenguajes incluidos en dicha plataforma. PyPy es un interprete/compilador de Python escrito en el propio Python (Es todavía un proyecto de investigación, basado en fondos de la Unión Europea). También hay que mencionar Stackless Python, una variante de la implementación en C que reduce la dependencia de la pila de C para las llamadas a funciones o métodos, permite corutinas, continuaciones y microthreads.


    Índice de artículos · Siguiente: La filosofía de diseño de Python

    0

    Añadir un comentario

  4. Historia de Python

    Desde hace un tiempo, Guido Van Rossum, creador del lenguaje de programación Python, está escribiendo una serie de artículos sobre la historia del lenguaje, desde sus primeras etapas hasta la actualidad. El blog se llama History of Python. Estos artículos combinan dos cosas que me son muy gratas, la historia y la programación, y encima sobre mi lenguaje favorito, así que sobra decir que estoy más que enganchado.

    Me gustan tanto que, hace tiempo también, le pedí permiso a Guido para traducir sus columnas, y lo curioso del caso es que me contestó, muy amablemente y al día siguiente. Así que me he animado y durante este tiempo he ido traduciendo sus columnas, que iré colgando por aquí, con un poco de suerte, cada jueves.

    Usaré este post como guía o índice, de forma que lo modificaré a medida que vaya traduciendo nuevas columnas, y las iré incluyendo en el siguiente listado:

    Sobra decir que cualquier error o malentendido provocado durante en la traducción es responsabilidad unicamente mía, y que se agradece cualquier comentario o aportación a este proyecto.

    6

    Ver comentarios

  5. Como generar contraseñas pronunciables

    En el trabajo tenemos un sistema de generación de contraseñas automáticas, que cuando creamos una cuenta de usuario escupe chorizos del estilo gpsiaqly34 o vhlyuyym782. Son contraseñas seguras, y el algoritmo de generación es trivial, pero resultan difíciles de recordar y de comunicar por voz; tienes que deletrearla, y normalmente comprobarla después, porque es muy fácil olvidar o confundir alguna letra.

    He modificado el sistema para que las contraseñas, manteniendo su carácter aleatorio, sean más pronunciables. Parto de un fichero con texto en español, y utilizo la técnica de las cadenas de Markov para elegir, dada una letra, la siguiente, teniendo en cuenta las frecuencias relativas con que una letra es seguida por otra.

    Es más fácil de entender con un ejemplo. Supongamos que partimos solo de estas 26 palabras: Abad, Baratillo, Cable, Debido, Educativo, Floritura, Galés, Hiato, Impureza, Jurista, Kárate, Laboriosidad, Madriguera, Negativo, Observatorio Pararrayos, Quinielista, Radio, Sabelotodo, Tabacal, Ultrasonido, Vendaval, Whisky, Xilofón, Yacer y Zafar. Lo que hacemos es calcular la probabilidad de que a una determinada letra, la siga otra. Por ejemplo, detrás de una 'a', partiendo de las palabras que tenemos, hay dos veces que viene una 'c', (en Tabacal y Yacer), en cuatro ocasiones viene una 'd' (en Abad, Laboriosidad, Madriguera y Radio), pero nunca aparece una 'e'.

    Con estos datos, la lista completa de frecuencias para la letra 'a' (Omitiendo los casos en que la frecuencia es cero) sería la siguiente:

    A la letra la sigue tantas veces Frecuencia
    a b 5 0.17241379
    a c 2 0.06896552
    a d 4 0.13793103
    a f 1 0.03448276
    a l 3 0.10344828
    a r 5 0.17241379
    a s 1 0.03448276
    a t 6 0.20689655
    a v 1 0.03448276
    a y 1 0.03448276

    El siguiente código genera una contraseña, partiendo de una URL de la cual obtiene las textos y las frecuencias correspondientes:

    
    import re, string
    import random
    import urllib2
    
    class PasswordGenerator(object):
        def __init__(self, url):
            tabla = {
                193:97,   # Á -> a
                201:101,  # É -> e
                205:105,  # Í -> i
                211:111,  # Ó -> o
                218:117,  # Ú -> u
                220:117,  # Ü -> u
                209:110,  # Ñ -> n
                225:97,   # á -> a
                233:101,  # é -> e
                237:105,  # í -> i
                243:111,  # ó -> o
                250:117,  # ú -> u
                252:117,  # ü -> u
                241:110,  # ñ -> n
                }
            data = urllib2.urlopen(url).read().lower().decode('utf-8').translate(tabla)
            patSep = re.compile('[^a-z]')
            words = patSep.split(data)
            self.stats = {}
            for word in words: 
                if len(word) > 1: 
                    for (index, letter) in enumerate(word[0:-1]):
                        next_letter = word[index+1]
                        self.stats.setdefault(letter, []).append(next_letter)
            self.semilla = self.stats.keys()
    
        def __call__(self, size=8):
            buff = [random.choice(self.semilla)]
            for i in range(size): # de size letras
                buff.append(random.choice(self.stats[buff[-1]]))
            return ''.join(buff)
    
    pw = PasswordGenerator('http://elornitorrincoenmascarado.blogspot.com/')
    print pw(size=10)
    

    Ejecutar este código me ha dado como respuesta orleflenoe. Por supuesto, si se ejecuta otra vez, el resultado será totalmente distinto. Algunos ejemplos de las contraseñas generadas con tamaño 9 son:

    • pelonarie
    • votezaral
    • ayatabera
    • vegarione
    • whiskyaya
    • cayorvada
    • vacabiska
    • puraboson

    De estas me gusta especialmente vacabiska. Es un hack rápido, mi idea es crear un fichero relativamente grande, que contenga unas 20.000 palabras en español, calcular las frecuencias relativas de todas las combinaciones de letras y guardarlas en una tabla para no tener que calcularlas cada vez.

    El sistema no es perfecto, claro está; algunas de estas contraseñas, como unujunarinc, pueden destrozar más de una laringe delicada.

    Extra Bonus: Se puede usar para crear nombres ficticios para nuestros relatos de ciencia ficción o fantasía: Adamana, de la tribu de Wanacos, hijo de Jostarr, hijo de Olelefl, navegó hasta la ciudad maldita de Editefo para buscar la espada de Rilinst y jurar venganza contra su enemigo inmortal, Thubono.

    11

    Ver comentarios

Archivo del blog
Etiquetas
Etiquetas
Enlaces interesantes
Cargando