A Django site.
Julio 9, 2009
» La historia de Python: El problema con la división entre enteros

El siguiente texto es una traducción del artículo How Everything Became an Executable Statement 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.

Junio 25, 2009
» 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.

Abril 30, 2009
» La historia de Python: Cómo todo se convirtió en sentencias ejecutables

El siguiente texto es una traducción del artículo How Everything Became an Executable Statement de Guido van Rossum publicado en http://python-history.blogspot.com/.

Cómo todo se convirtió en sentencias ejecutables

Los nuevos usuarios de Python a veces se sorprenden al descubrir que todas las partes del lenguaje son sentencias ejecutables, incluyendo la definición de funciones y clases. Eso significa que cualquier sentencia puede aparecer en cualquier lugar en un programa. Por ejemplo, una definición de una función puede aparecer dentro de una sentencia “if” si así se lo desea.

En una versión muy temprana de la gramática de Python esto no era así: los elementos de la gramática tenían un “sabor decorativo”, las sentencias import y la definición de funciones solo eran permitidas en el nivel superior de un módulo o script (dónde eran ejecutadas para efectivizarse).

De todas formas, cuando estaba agregando soporte para clases, decidí que esto era muy restrictivo.

Mi razonamiento fue más o menos como sigue. En lugar de definir el cuerpo de una clase sólo como una serie de declaraciones de funciones, también parecía adecuado permitir asignaciones a variables allí. De todas formas, si iba a permitir eso, ¿por qué no ir un escalón más arriba y permitir código ejecutable arbitrario? O, llevando esto aún más lejos, ¿por qué no permitir declaración de funciones dentro de sentencias “if”, por ejemplo? Rápidamente se vio que esto permitía una simplificación de la gramática, ya que ahora todos los usos de sentencias (estén identados o no) podían compartir la misma regla de gramática, y de hecho el compilador podría usar la misma función generadora de byte code para todas ellas.

A pesar de que este razonamiento me permitía simplificar la gramática y los usuarios podían colocar sentencias Python en cualquier lugar, esta característica no habilitaba necesariamente ciertos estilos de programación. Por ejemplo, la gramática de Python técnicamente permitía a los usuarios escribir cosas como funciones anidadas aunque la semántica subyacente de Python no aceptara ámbitos anidados. Por lo tanto, el código así operaría de formas inesperadas o “rotas” comparadas con lenguajes que realmente estaban diseñados con esa característica en mente. Con el paso del tiempo, muchas de esas características “rotas” se arreglaron. Por ejemplo, la definición de funciones anidadas sólo empezó a funcionar un poco más correcta en Python 2.1.
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.

Abril 25, 2009
» La historia de Python: Todo de primera clase

El siguiente texto es una traducción del artículo First-class Everything de Guido van Rossum publicado en http://python-history.blogspot.com/.

Todo de primera clase

Uno de mis objetivos para Python era hacerlo de tal forma que todos los objetos sean de “primera clase”. Con esto me refiero a que quería que todos los objetos puedan ser nombrados en el lenguaje (por ejemplo, enteros, strings, funciones, clases, módulos, métodos, etc.) para tener igual status. Entonces pueden ser asignados a variables, ubicados en listas, almacenados en diccionarios, pasados como argumentos y más.

La implementación interna de Python hizo que esto sea fácil de hacer. Todos los objetos de Python estaban basados en una estructura de datos de C común que se usaba en todos los lugares del intérprete. Variables, listas, funciones y todo lo demás usaba variaciones de esta estructura de datos; directamente no importaba si la estructura representaba un objeto simple como un entero o algo más complicado como una clase.

Aunque la idea de tener “todo de primera clase” es conceptualmente simple, había aún un aspecto de las clases que necesitaba resolver; el problema de hacer que los métodos sean objetos de primera clase.

Consideremos esta clase simple en Python (copiada de la entrada de la semana pasada):

class A:
    def __init__(self,x):
        self.x = x
    def spam(self,y):
        print self.x, y

Si los métodos van a ser objetos de primera clase, entonces pueden ser asignados a otras variables y usados como cualquier otro objeto en Python. Por ejemplo, alguien podría escribir una sentencia en Python como “s = A.spam”. En este caso la variable “s” referencia un método de una clase, que en realidad es solo una función. Sin embargo, un método no es exactamente igual a una función. En concreto, se supone que el primer argumento de un método es una instancia de la clase en la que el método fue definido.

Para tratar esto cree un tipo de objeto invocable (callable) conocido como “unbound method”. Un unbound method era en realidad un wrapper delgado alrededor de un objeto función que implementaba un método, pero forzaba la restricción de que el primer argumento tenía que ser una instancia de la clase en la cual el método fue definido. Así, si alguien quería llamar al unbound method “s” como una función, tendrían que pasar una instancia de la clase “A” como primer argumento. Por ejemplo, “a = A(); s(a)”.(*)

Un problema relacionado ocurre si alguien escribe una sentencia Python que refiere al método en una instancia específica de un objeto. Por ejemplo, alguien puede crear una instancia usando “a = A()” y luego escribir una sentencia como “s = a.spam”. Aquí la variable “s” nuevamente referencia al método de una clase, pero la referencia a ese método se obtuvo a través de la instancia “a”. Para manejar esta situación se usa un objeto invocable diferente llamado “bound method”. Este objeto es también un wrapper delgado alrededor del objeto función para el método. Sin embargo, este envoltorio implícitamente almacena la instancia original que fue usada para obtener el método. Así, una sentencia futura como “s()” llamará al método implícitamente con la instancia “a” como el primer argumento.

En realidad el mismo objeto interno es usado para representar los bound y unbound methods. Uno de los atributos de este objeto contiene una referencia a una instancia. Si es None, el método es unbound. De otro modo, el método es bound.

A pesar de que bound y unbound methods parezcan un detalle sin importancia, son una parte crítica de como las clases funcionan bajo el tapete. Siempre que una sentencia como “a.spam()” aparece en un programa, la ejecución de la sentencia ocurre en dos partes. Primero ocurre la búsqueda de “a.spam”. Esto retorna un bound method; un objeto invocable. Luego, una operación de llamado de función “()” es aplicada a ese objeto para invocar el método con los argumentos provistos por el usuario.

(*) En Python 3000, el concepto de unbound methods se eliminó y la expresión “A.spam” retorna un objeto función normal. Nos dimos cuenta de que la restricción de que el primer argumento sea una instancia de A ayudaba pocas veces al diagnosticar problemas y frecuentemente era un obstáculo para usos avanzados; alguien lo llamó “duck typing self”, el cual parece un nombre apropiado.

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.

Abril 20, 2009
» La historia de Python: Clases definidas por los usuarios

El siguiente texto es una traducción del artículo Adding Support for User-defined Classes de Guido van Rossum publicado en http://python-history.blogspot.com/.

Añadir clases definidas por los usuarios

Crease o no, las clases fueron un añadido tardío durante el primer año del
desarrollo de Python, todavía en el CWI, aunque bastante antes de la primera
versión pública. En cualquier caso, para entender como se añadieron las clases,
ayuda saber un poco más sobre los detalles de implementación de Python.

Python está escrito en C en forma de un intérprete de código intermedio o
pseudo-binario (bytecode), usando la clásica estructura de pila, junto con una
colección de tipos primitivos, también implementados en C. La arquitectura
subyacente usa “objetos”, pero como C no soporta objetos directamente, se
implementan usando estructuras de objetos y punteros a funciones. La máquina
virtual Python define docenas de operaciones estándar que cada objeto debe o
puede implementar (por ejemplo, get_attribute, add y call).

Un objeto se representa mediante una estructura estática que contiene una serie
de punteros a funciones, uno para cada operación estándar. Estos punteros son
inicializados normalmente con referencias a funciones estáticas. Pero algunas
operaciones son opcionales y un objeto puede dejar esas entradas apuntando a
NULL si decide no implementar la función. En este caso, la máquina virtual
o bien genera un error en tiempo de ejecución o, en determinadas circunstancias,
puede que proporcione una implementación por defecto de la operación. La estructura
C contiene también varios campos de datos, uno de los cuales es una referencia a
la lista de métodos adicionales que son únicos para ese tipo de datos, representada
como una matriz de estructuras que constan de un texto (el nombre del método) y
un puntero a una función (la implementación). El enfoque a la introspección de
Python deriva de esta habilidad de hacer que la propia estructura del tipo sea
accesible en tiempo de ejecución, como cualquier otro objeto.

Un aspecto importante de esta implementación es que está completamente centrada
en el lenguaje C. De hecho, todas las operaciones y los métodos estándar están
implementados por funciones en C. En un principio, el interprete de bytecode
solo soportaba llamadas a funciones escritas en Python puro y funciones o métodos
implementados en C. Creo que fue mi colega Siebren van der Zee el primero en
sugerir que Python debería permitir definiciones de clases similares a las
de C++, que permitieran al programador crear objetos propios.

Para poder implementar estos objetos de usuario, me ceñí al diseño más simple
que pude imaginar: un esquema donde los objetos de usuario se representarían por
nuevos objetos que almacenarían una referencia de clase que apuntaría
a un “objeto clase” compartido por todas las instancias de la misma clase, y un
diccionario, bautizado “diccionario de instancia”, que contendría las variables
particulares de cada instancia.

En esta implementación, el diccionario de la instancia contendría los valores de
las variables de cada instancia, mientras que el objeto clase contendría la
información que fuera compartida entre todas las instancias de la misma clase,
especialmente, los métodos. Al implementar la clase objeto opté de nuevo por el
diseño más sencillo posible; el conjunto de métodos de la clase se almacenaría
en un diccionario, cuyas claves serían los nombres de los métodos, con lo que se
creó el diccionario de la clase. Para implementar la herencia, los objetos clase
almacenarían opcionalmente una referencia a los objetos clase correspondientes a
las clases base. En esa época era bastante ingenuo en lo que se refería a las
clases, pero sabía que existía la herencia múltiple, que C++ había incorporado
recientemente. Decidí que si iba a implementar la herencia, bien podría
implementar una versión simplificada de la herencia múltiple, de forma que una
clase pudiera derivar de más de una clase base.

En esta implementación, los mecanismos subyacentes que gestionaban los objetos
eran en realidad muy simples. Cualquier cambio hecho a las variables, ya sea de
clase o de instancia, se verían reflejados en el objeto diccionario respectivo.
Por ejemplo, asignar un valor a una variable de una instancia actualizaría su
diccionario local. De igual forma, cuando buscáramos el valor de una variable de
instancia de un objeto, simplemente miramos en el diccionario subyacente. Si la
variable no se encuentra allí, las cosas se ponen un poco más interesantes. En
ese caso, las búsquedas deben realizarse en el diccionario asociado a la clase,
y si tampoco se encontrara allí, en los diccionarios de cada clase de la que
derive.

Es más habitual ver este mecanismo de búsqueda de atributos en la clase del
objeto, así como en sus clases antecesoras, en el caso de la búsqueda de
métodos. Como se ha mencionado anteriormente, los métodos se almacenan en el
diccionario de la clase, por lo que son compartidos por todas las instancias de
objetos pertenecientes a dicha clase. Así, cuando se invoca un método, lo normal
es que no lo encuentres en el diccionario local del objeto. En vez de eso, se
busca el método en la clase del objeto, y de no encontrarse, su busca
sistemáticamente por todas las clases de las que deriva hasta encontrarlo. Cada
una de las clases básicas implementa el mismo algoritmo recursivo. Esto se
conoce habitualmente como la regla de primero en profundidad, luego de derecha a
izquierda, y ha sido el método de ordenación y selección de métodos (MRO -
Method Resolution Order) usado por Python en la mayoría de sus versiones.
Las versiones más modernas han adoptado un MRO más sofisticado, que se
discutirá en un futuro artículo de esta serie.

Al implementar las clases, uno de mis objetivos fue mantener las cosas sencillas.
Así, Python no realiza comprobaciones de errores ni comprueba inconsistencias a
la hora de localizar métodos. Por ejemplo, si una clase sobreescribe un método
definido en una clase antecesora, no se realiza ninguna comprobación para
verificar que el método redefinido tenga el mismo número de argumentos, ni que
puede ser llamada de la misma manera que el método original. El algoritmo de
resolución y localización de métodos se limita a devolver el primer método que
encuentre, y lo ejecuta con cualesquiera argumentos que haya indicado el usuario.

A partir de este diseño emergieron otras características. Por ejemplo, aunque el
diccionario de clase se pensó inicialmente como un repositorio de métodos, no
existía ninguna razón que le impidiera contener también otros tipos de objetos.
Así, objetos como números enteros o cadenas de texto podían ser almacenados en
el diccionario de la clase, lo que los convertía a todos los efectos en
variables de clase; variables que son compartidas por todas las instancias de
una determinada clase, en vez de estar almacenadas localmente.

Aunque la implementación era sencilla, también proporcionaba un alto grado de
flexibilidad. Por ejemplo, la implementación hacía que las propias clases fueran
objetos, en pie de igualdad con cualquier otro objeto (objetos de primera clase,
o first-class objects, como se les suele describir en la documentación),
lo que significaba que podían ser inspeccionadas de forma introspectiva en
tiempo de ejecución, e incluso ser modificadas dinámicamente. Se podían añadir o
modificar métodos simplemente actualizando el diccionario de la clase, una vez
que la clase hubiera sido creada (*). La naturaleza dinámica de Python
significaba que esos cambios tendrían un efecto inmediato en todas las instancias
de esa clase o de sus clases derivadas. De igual manera, se podía modificar
dinámicamente objetos individuales añadiendo, modificando o borrando variables
de instancia (una característica que, como comprendí posteriormente, hacía que
la implementación de clases y objetos de Python fuera más permisiva que la de
Smalltalk, que restringía el conjunto de atributos a aquellos especificados en
el momento de la creación).

Desarrollo de la sintaxis de clases

Habiendo diseñado las representaciones en tiempo de ejecución para las clases
definidas por el usuario, mi siguiente tarea era diseñar la sintaxis para las
definiciones de clases, y en particular, para las definiciones de métodos dentro
de la clase. Había una restricción fuerte y era que yo no quería que la sintaxis
para definir métodos fuera distinta de la sintaxis para definir funciones.

Reconstruir la gramática y el generador de bytecode para manejar estos
dos casos tan similares de forma diferente fue una tarea ardua. Aun así, aunque
conseguí mantener la gramática igual, aún tenía que encontrar la manera de
tratar con las variables de instancia. Inicialmente había esperado emular las
variables de instancia implícitas que podemos ver, por ejemplo, en C++. En ese
lenguaje, las clases se definen con un código como el siguiente:

    class A {
    public:
       int x;
       void spam(int y) {
            printf("%d %d\n", x, y);
       }
    };

En esta clase se ha declarado la variable de instancia x. En los métodos,
las referencias a x se refieren implícitamente a la variable de instancia.

Por ejemplo, en el método spam(), no se declara la variable x ni
como parámetro, ni como variable local, pero como la clase ha declarado una
variable de instancia del mismo nombre, se asume que las referencias a x
se refieren a dicha variable. Aunque deseaba proporcionar a Python algo similar,
pronto me di cuenta de que esta aproximación sería imposible, ya que, en un
lenguaje que carece de declaración de variables, no habría una manera elegante
de distinguir las variables de instancia de las variables locales.

En teoría, obtener el valor de las variables de instancia debería ser bastante
fácil. Python ya disponía de un orden de búsqueda predefinido para nombres de
variables no cualificados: locales, globales e internas (built-ins).
Cada una de estas áreas estaba representada por un diccionario que mapeaba los
nombres de las variables con sus valores. Cada referencia a una variable se
convertía, así, en una serie de búsquedas en diccionarios que concluía cuando se
encontrada el nombre de la variable. Por ejemplo, durante la ejecución de una
función con una variable local p y una variable global q, en una
sentencia como, por ejemplo, print p, q buscaría p en el primer
diccionario, el de las variables locales, y lo encontraría. Luego buscaría q
en ese mismo diccionario y no lo encontraría, por lo que continuaría la búsqueda
por el segundo diccionario, el de las variables globales, hasta encontrarlo.

Habría sido muy fácil añadir el diccionario de instancia del objeto actual al
principio de esta lista de diccionarios a la hora de ejecutar un método. De esa
forma, en un método de un objeto con una variable de instancia x y una
variable local y, una sentencia como print x,y encontraría x
en el diccionario de la instancia (el primer diccionario según la nueva
ordenación), e y en el diccionario de variables locales (el segundo
diccionario).

El problema con esta estrategia es que fracasa al intentar declarar los valores
de las variables de instancia. La asignación en Python no busca el nombre de la
variable en los diccionarios, sino que se limita a añadir o reemplazar la
variable en el primer diccionario de la lista, normalmente el de variables
locales. Esto provoca que las variables siempre se creen en el ámbito local, si
no se especifica nada (aunque hay que hacer notar que existe una “declaración
global” que invalida este comportamiento para una variable dentro de una función).

Si no cambiamos esta aproximación minimalista a la asignación, el que el
diccionario de la instancia fuera el primero en la lista de búsqueda haría
imposible asignar valores a las variables locales dentro de un método. Por
ejemplo, si tuviéramos un método así:

    def spam(y):
        x = 1
        y = 2

Las asignaciones a x e y sobreescribirían el valor de la variable
de instacia x y crearían una nueva variable de instancia y, que
impediría acceder al valor de la variable local y. Cambiar el orden de
los diccionarios (pasar el de instacia al segundo lugar y que el diccionario
local se convirtiera en el primero) simplemente la daría la vuelta al problema,
haciendo imposible realizar asignaciones a variables de instancia.

Tampoco funcionaría cambiar la semántica de las asignaciones para usar una
variable de instancia, si existe alguna, o usar una variable local en caso
contrario, porque esto nos crearía un problema de auto-referencias: ¿cómo
crearíamos una variable de instancia, en primer lugar? Una posible solución
podría ser obligar a declarar explícitamente las variables de instancia, de
forma similar a la usada para declarar variables globales, pero no quería añadir
una característica como esta, habiendo llegado tan lejos como había llegado sin
requerir ninguna declaración de variables. Además, la especificación extra para
indicar una variable global era un caso especial que apenas se usaba en la
mayoría del código. La declaración explícita de variables de instancia, por otro
lado, tendría que ser usada en prácticamente cualquier definición de clase. Otra
posible solución era distinguir lexicamente las variables de instancia. Por
ejemplo, usando un símbolo especial como el caracter @ (una aproximación tomada
por ruby) o usando alguna convención de nombres que implicara prefijos o un uso
particular de mayúsculas y minúsculas. Ninguna de estas opciones me agradaba (y
sigue sin hacerlo).

En vez de esto, decidí abandonar la idea de referencias implícitas a las
variables de instancia. Los lenguajes como C++ permiten escribir cosas como
this->foo, para señalar explícitamente que la variable foo es de
instancia, distinguiéndola así de una posible variable local foo. Decidí,
por tanto, hacer que la única manera de acceder a las variables de instancia
fueran estas referencias explícitas. Además, tomé la decisión de que this,
la variable que representaba al objeto actual, no fuera una palabra clave,
simplemente haría que this (o su equivalente) fuera un primer argumento
de cada método. Las variables de instancia sería siempre atributos de ese
argumento.

Usando referencias explícitas, no había ninguna necesidad de tener una sintaxis
especial para la definición de métodos, ni tenía uno que complicarse con
semánticas adicionales para la búsqueda de variables. En vez de eso, simplemente
se definía una función, sabiendo que el primer argumento correspondería con
el objeto instanciado. Por convención, se suele dar a este primer
argumento el nombre de self. Por ejemplo::

    def spam(self,y):
        print self.x, y

Esta aproximación recuerda algo a Modula-3, que ya me había proporcionado la
sintaxis para las importaciones y para el manejo de excepciones. Modula-3 no
tenía clases, pero permitía definir tipos estructurados que podían contener
punteros a funciones, que eran inicializadas por defecto con funciones definidas
previamente y añadía azúcar sintáctico para que, si x era una estructura
de ese tipo y m un puntero a una función almacenada en dicho registro,
inicializado a una función f, entonces llamar a x.m(args) equivalía
a llamar a f(x, args). Esto se ajusta a la implementación de objetos y
métodos, y hace posible equiparar las variables de instancia con atributos del
primer argumento.

El resto de los detalles de la sintaxis de Python para clases se derivan de este
diseño o de las demás restricciones impuestas por la implementación. Siguiendo
con mis aspiraciones de sencillez, imaginaba la sentencia class como una
serie de definiciones de métodos, que son sintácticamente iguales a las
definiciones de funciones, aun cuando se estableciera por convención que todas
deberían tener un primer argumento llamado self. Además, en vez de
desarrollar una nueva sintaxis para los métodos especiales (como los
constructores y los destructores), tomé la decisión de que estos casos se
resolverían obligando al usuario a utilizar nombres especiales, como __init__,
__del__ y demás. Esta convención de nombres se tomó del lenguaje C, en el
que los identificadores que empezaban con el caracter guión bajo estaban
reservados para el compilador y tenían, a menudo, significados especiales
(por ejemplo, macros como __FILE__ en el preprocesador de C).

Así, la visión que tenía del código para definir una clase era esta:

    class A:
         def __init__(self,x):
             self.x = x
         def spam(self,y):
            print self.x, y

También quería seguir reutilizando la máxima cantidad posible de código.
Normalmente, una definición de una función es una sentencia ejecutable que,
simplemente, realiza una asignación; asigna a una variable, en el espacio de
nombres local, el objeto función (el nombre de la variable será, por tanto, el
nombre de la función). Se me ocurrió que, en vez de inventar una solución
distinta, era razonable hacer la misma interpretación para las definiciones
de métodos dentro del cuerpo de la clase, simplemente usando como espacio de
nombres un nuevo diccionario. Este nuevo diccionario sería entonces tratado y
usado para inicializar el diccionario de la clase, creando de esa forma una
nueva clase. Detrás de escena, la estrategia que se implementó fue
convertir el cuerpo de la clase en una función anónima, que ejecutaba todas las
sentencias de definición de métodos que encontrara en el cuerpo de la clase, y
que terminaba devolviendo un diccionario con todas las variables/métodos
definidas. Este diccionario se pasaba a una función auxiliar, que creaba la
clase en sí. Finalmente, el objeto que definía la propia clase se almacenaba en
una variable en el entorno local, siendo su nombre el mismo que el de la clase.

Los usuarios de Python a menudo se sorprenden al comprender que cualquier
sentencia válida de Python puede aparecer en el cuerpo de una clase. Esta
característica era en realidad una extensión de mi deseo de mantener la sintaxis
lo más limpia posible, a la vez que trataba de no limitar artificialmente
aquellas cosas que pudieran resultar útiles.

Un detalle final acerca de la sintaxis usada para instanciar objetos de una
clase. Otros lenguajes, como C++ o Java, usan para crear objetos un operador
especial, new. En C++ esta opción es defendible, porque los nombres de
las clases tienen un estatus especial para el analizador, pero en Python eso no
era así. Como el analizador de Python no se preocupa en absoluto por el tipo de
objeto que esta llamando, hacer que la propia clase fuera ejecutable era la
solución correcta, “mínima” en el sentido de que no requería una nueva sintaxis.
Creo que me adelanté un poco a los tiempos aquí; a día de hoy, el “patrón de
diseño Factory” es a menudo el sistema más empleado para la creación de
instancias y lo que yo hice fue simplemente convertir cada clase en su propia
fábrica (Factory).

Métodos especiales

Como decía en la última sección, uno de los objetivos que perseguía era que la
implementación de las clases fuera sencilla. En los demás lenguajes orientados
a objetos, normalmente existe una diversidad de métodos y operadores
especiales que sólo se aplican a las clases. Por ejemplo, en C++, hay una
sintaxis especial para definir constructores y destructores, diferente de la
usada para definir funciones o métodos normales.

En realidad, no quería introducir una nueva sintaxis para manejar las
operaciones especiales con los objetos. Así que me las arreglé para mapear los
operadores específicos con un conjunto de nombres especiales de métodos, como
__init__ y __del__. Los usuarios podrían definir su propio código
asociado a la creación y destrucción de objetos, simplemente definiendo métodos
con estos nombres especiales.

Usé la misma técnica para permitir a los usuarios redefinir el comportamiento de
los operadores de Python. Como ya se ha dicho, Python está escrito en C y
usa tablas que contienen punteros a funciones para implementar diferentes
capacidades de los objetos internos (por ejemplo, get attribute, add
y call). Para permitir que el usuario pudiera definir estas mismas
capacidades en sus clases, mapeé los punteros a diferentes funciones con nombres
especiales como __getattr__, __add__ y __call__.

Existe una correspondencia directa entre estos nombres y las tablas de punteros
de funciones que uno tiene que definir cuando se implemente un nuevo tipo de
objeto en C.

(*) Eventualmente, el nuevo estilo de clases hace que sea necesario controlar
los cambios en el __dict__ de la clase; aún se puede modificar dinámicamente
las clases, pero se debe utilizar asignación de atributos en lugar de la
variable __dict__ directamente.

Traducido por Juan I. Rodriguez.
Revisado por Juan José Conti y 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.

Abril 4, 2009
» La historia de Python: El uso de tipado dinámico

El siguiente texto es una traducción del artículo Python’s Use of Dynamic Typing de Guido van Rossum publicado en http://python-history.blogspot.com/.

El uso de tipado dinámico en Python

Una diferencia importante entre ABC y Python es el estilo general del sistema de tipos. ABC es estáticamente tipado, lo cual significa que el compilador de ABC analiza el uso de tipos en un programa y decide si están siendo usados consistentemente. Si no, el programa es rechazado y su ejecución no puede comenzar. A diferencia de la mayoría de los lenguajes con tipado estático de esos días, ABC usaba inferencia de tipos (no distinto que Haskell) en lugar de declaraciones explícitas de tipos como en C. En contraste, Python es dinámicamente tipado. El compilador de Python ignora felizmente los tipos usados en un programa y todo el control de tipos es hecho en tiempo de ejecución.

Aunque esto pueda parecer muy distinto de ABC, no es tan diferente como uno imaginaría. A diferencia de otros lenguajes de tipado estático, ABC no depende (¿dependía? es prácticamente histórico hoy : - ) exclusivamente de controles de tipado estático para evitar que el programa termine abruptamente, también tiene una librería en tiempo de ejecución que controla los tipos de los argumentos en todas las operaciones nuevamente cada vez que son ejecutadas. Esta verificación no estaba de más para los algoritmos de control de tipos del compilador, que no estaban totalmente implementados en el primer prototipo del lenguaje. La librería en tiempo de ejecución también servía como una ayuda para la depuración, ya que el control de tipos explícito en tiempo de ejecución puede producir lindos mensajes de error (algo requerido por los implementadores), en lugar de los vuelcos de memoria que sucederían si el intérprete siguiera ciegamente con una operación sin controlar si los argumentos tienen sentido.

Sin embargo, la razón más importante por la que ABC tenía control de tipos en tiempo de ejecución, además de control de tipado estático, es su naturaleza interactiva. En una sesión interactiva, el usuario tipea sentencias de ABC y definiciones que son ejecutadas tan pronto como son completadas. En una sesión interactiva, es posible crear una variable y asignarle un número, borrarla y luego volver a crearla (en otras palabras, crear otra variable con el mismo nombre) y asignarle un string. Dentro de un solo procedimiento, sería un error de tipado estático usar el mismo nombre de variable primero como un número y luego como un string, pero no sería razonable forzar ese control entre diferentes sentencias entradas en una sesión interactiva, mientras que la creación accidental de una variable llamada x a la que se le asigna un número, !prohiba para siempre la creación de una variable x con otro tipo!. El compromiso de ABC es usar control de tipos dinámico para las variables globales, pero estático para las locales. Para simplificar la implementación, las variables locales obtienen control de tipo dinámico también.

Así, hay solo un pequeño paso desde el enfoque usado en la implementación de ABC para el control de tipos al de Python; Python simplemente deja todo el control de tipos en tiempo de compilación. Esto se alinea completamente con la filosofía de Python de “tomar atajos”, ya que simplifica la implementación y no afecta la eventual seguridad, ya que todos los errores de tipo son atrapados en tiempo de ejecución antes de que causen un mal funcionamiento del intérprete de Python.

Sin embargo, una vez que te decides por el tipado dinámico no hay vuelta atrás. Las operaciones de ABC fueron cuidadosamente diseñadas para que el tipo de los argumentos pueda ser deducido de la forma de los operadores. Por ejemplo, de la expresión “x^y” el compilador deduciría que las variables x e y son strings, así como el resultado. En Python, esa deducción no se puede generalizar. Por ejemplo, la expresión “x+y” puede ser una concatenación de strings, una suma entre números, o una operación sobrecargada sobre tipos definidos por el usuarios.

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.

Marzo 18, 2009
» La historia de Python: El principio del diseño y desarrollo del lenguaje

El siguiente texto es una traducción del artículo Early Language Design and Development Guido van Rossum publicado en http://python-history.blogspot.com/.

El principio del diseño y desarrollo del lenguaje

De ABC a Python

La primera y principal influencia de Python fue ABC, un lenguaje diseñado a principios de los 80 por Lamber Meertens, Leo Geurts y otros en CWI. El objetivo de ABC era ser un lenguaje de enseñanza, un reemplazo para BASIC, y un lenguaje y entorno para computación personal. Fue diseñado en un principio haciendo un análisis de la tarea de programar y luego haciendo varias iteraciones que incluían pruebas de usuario a conciencia. Mi rol en el grupo de ABC era principalmente implementar el lenguaje y su entorno integrado de edición.

El uso que Python hace de la identación viene directamente de ABC, pero esta idea no se originó con ABC (ya había sido promovida por Donald Knuth y era un concepto bien conocido de estilo de programación). (El lenguaje de programación occam también lo usaba). Si embargo, los autores de ABC sí inventaron el uso de los dos puntos que separa la cláusula inicial del bloque identado. Luego de las primeras pruebas con usuarios sin los dos puntos, se descubrió que el significado de la identación no le quedaba claro a los principiantes que tomaban sus primeras lecciones de programación. Agregar los dos puntos clarificó su significado: los dos puntos de alguna formar guiaban la atención a lo que seguía y unía lo anterior con lo siguiente de forma correcta.

Los principales tipos de datos de Python también vienen de ABC, aunque con algunas modificaciones. Las listas en ABC eran en realidad bags o multisets, que siempre se mantenían ordenadas utilizando una implementación modificada de árboles B. Sus tablas eran arrays asociativos que se mantenían ordenados en forma similar mediante claves. Encontré que ningún tipo de dato era preciso para representar, por ejemplo, la secuencia de líneas leídas de un archivo, el cual anticipé que sería un caso de uso común (en ABC tenías que usar una tabla con el número de línea como clave, pero eso complicaba las inserciones y los borrados). Entonces convertí el tipo lista en un array flexible con operaciones de inserción y borrado, dándole a los usuarios control total sobre el orden de los elementos en una lista. Un método sort soportaba la necesidad ocasional de resultados ordenados.

También reemplacé las tablas ordenadas implementando una tabla hash. Elegí una tabla hash porque creía que sería más rápida y fácil de implementar que el árbol B de ABC. Estaba teóricamente probado que los árboles B eran asintóticamente óptimos en tiempo y espacio para una gran variedad de operaciones, pero en la práctica se volvieron difíciles de implementar correctamente debido a la complejidad de sus algoritmos. Por la misma razón, la performance tampoco era óptima para tablas pequeñas.

Mantuve el tipo de dato inmutable de ABC llamado tupla (las operaciones de empaquetado y desempaquetado en Python vienen directamente de ABC). Ya que las tuplas son implementadas mediante arrays, decidí agregarles indexación y rebanado.

Una consecuencia de añadirle una interfaz de tipo array a las tuplas fue que tuve que pensar en una forma de resolver los casos límites de tuplas de longitud 0 ó 1. Una de las reglas que tomé de ABC fue que cada tipo de datos, al ser impreso o convertido a string, debía ser representado por una expresión que sea una entrada válida para el parser del lenguaje. De esto siguió que necesitaba notaciones para las tuplas de longitud 0 y 1. Al mismo tiempo no quería perder la distinción entre una tupla y una expresión entre paréntesis, entonces utilicé un enfoque feo pero pragmático en el cual una coma final convertiría una expresión en una tupla de un elemento y “()” representaría a una tupla de cero elementos. Vale la pena mencionar que los paréntesis por lo general no son necesarios en la sintaxis de Python, excepto aquí (representar la tupla vacía con “nada” podría fácilmente enmascarar errores genuinos).

Los strings de Python empezaron con una semántica (inmutable) muy parecida a los strings de ABC, pero con una notación diferente e indexación basada en 0. Ya que ahora tenía tres tipos indexables -listas, tuplas y strings- decidí generalizar todo en un concepto común, la secuencia. Esta generalización hizo que ciertas operaciones básicas como obtener la longitud (len(s)), indexar (s[i]), rebanar (s[i:j]) e iterar (for i in s) funcionen de la misma forma en cualquier tipo que sea una secuencia.

Los números son uno de los puntos en los que más en desacuerdo estuve con ABC. ABC tenía dos tipos de números en tiempo de ejecución; los números exactos que eran representados como números racionales de precisión arbitraria y los números aproximados que eran representados mediante punto flotante binario con un rango de exponente extendido. Los números racionales no encajaban en mi visión del tema (anécdota: una vez intenté computar mis impuestos usando ABC. El programa, que parecía bastante directo, estaba demorando mucho en computar unos pocos números. Luego de investigar descubrí que estaba haciendo aritmética con números con miles de dígitos de precisión, que tenían que ser redondeados a florines -pie 100 centavos holandeses - y centavos para ser impresos). Es por esto que para Python elegí un modelo más tradicional con enteros de máquina y punto flotante binario de máquina. En la implementación de Python, estos números son representados simplemente con los tipos de datos de C long y double respectivamente.

Creyendo que también había un caso de uso importante para números exactos sin límite, agregué un tipo de dato bignum, que llamé long. Ya tenía una implementación de bignum que había sido el resultado de un intento inconcluso por mejorar la implementación de ABC unos años antes (la implementación original de ABC, una de mis primeras contribuciones, usaba una representación decimal internamente). Sonaba lógico usar este código en Python.

A pesar de haber agregado bignums a Python, es importante enfatizar que no quería usar bignums para todas las operaciones entre enteros. De extrapolar lo que veía en programas escritos por mí y por colegas en CWI, sabía que las operaciones entre enteros representaban una porción significativa del total del tiempo que la mayoría de los programas corrían. El uso más común de los enteros es indexar secuencias que entran en memoria. Así, decidí usar enteros de máquina para los casos de uso más comunes y el rango extra de bignums solo para hacer “matemática seria” o calcular la deuda externa de Estados Unidos en peniques.

El problema con los números

La implementación de números, especialmente enteros, es un área en la que cometí varios errores de diseño serios, pero también aprendí lecciones importantes sobre el diseño de Python.

Ya que Python tiene dos tipos diferentes de enteros, necesitaba una forma de distinguir entre los dos tipos en un programa. Mi solución era pedirle a los usuarios que explícitamente digan cuando querían usarlos agregando una L al final de los números (por ejemplo 1234L). Esta es un área en la que Python violaba la filosofía inspirada en ABC de no necesitar que los usuarios se encargar de detalles de implementación que no les importaban.

Lamentablemente, este era solo el menor detalle de un problema mayor. Un error más ilustre fue que mi implementación de enteros y longs ¡tenía una ligera diferencia semántica en algunos casos! Ya que el tipo int era representado como un entero de máquina, las operaciones que desbordaban silenciosamente recortaban el resultado a 32 bits o a la precisión que el tipo long de C tuviera. Además, el tipo int, que normalmente se considera tiene signo, era tratado como sin signo por las operaciones bitwise y shift y en la conversión desde/hacia octales o hexadecimales representados como int o long. Los longs, por otro lado, siempre se consideraban con signo. Por lo tanto, algunas operaciones producían un resultado diferente, dependiendo de si un argumento era representado como int o como long. Por ejemplo, en una aritmética de 32 bits, 1<<31 (1 shift a izquierda 31 bits) produciría el entero negativo más grande de 32 bits y 1<<32 produciría cero, mientras que 1L<<31 (1 representado como long shift a izquierda 31 bits) produciría un entero enorme igual a 2**31 y 1L<<32 produciría 2**32.

Para resolver algunos de estos asuntos hice un arreglo simple. En lugar de tener operaciones entre enteros que recorten silenciosamente el resultado, cambié la mayoría de las operaciones aritméticas para que lancen una excepción OverflowError cuando el resultado no encaje. (La única excepción a este control eran las operaciones de “bit-wise” mencionadas anteriormente, ya que asumí que los usuarios esperarían que estas operaciones se comporten como en C). Si no hubiese añadido este control, los usuarios de Python indudablemente hubiesen empezado a escribir código dependiente de la semántica de la aritmética binaria con signo de módulo 2**32 (como hacen los usuarios de C), y arreglar el error hubiese sido una transición mucho más dolorosa para la comunidad.

A pesar de que la inclusión del control de desborde pueda parecer un detalle de implementación menor, una dolorosa experiencia de debugging me hizo dar cuenta que era una característica útil. Como uno de mis primeros experimentos en Python, intenté implementar un algoritmo matemático simple, el computo de los “Números de Meertens”, un poco de matemática recreativa inventada por Richard Bird al celebrar los 25 añosen WCI del principal autor de ABC. Los primeros números de Meertens son pequeños, pero al traducir el algoritmo en código no me había dado cuenta de que los resultados intermedios del computo eran mucho más grandes que 32 bits. Me llevó una larga y dolorosa sesión de debugging descubrir esto, y decidí entonces manejar el asunto controlando todas las operaciones entre enteros y lanzando una excepción siempre que el resultado no pueda ser representado como un long de C. El costo extra del control de desborde no se notaría junto a la sobrecarga que ya tenía con la decisión de implementación de crear un nuevo objeto para el resultado.

Lamentablemente, siento decir que lanzar una excepción por desborde ¡tampoco era la solución correcta! En ese entonces, estaba trabado por la regla de C “las operaciones con tipos numéricos T retornan un resultado de tipo T”. Esta regla también era la razón de mi otro gran error en la semántica de los enteros: truncar el resultado de la división entre enteros, que discutiré en una de las próximas entradas. En retrospectiva, debí hacer que las operaciones entre enteros que desbordaban cambien el tipo de su resultado a long. Esta es la forma en que Python funciona hoy, pero completar esta transición llevó mucho tiempo.

A pesar del problema con los números, una cosa muy positiva salió de esta experiencia. Decidí que no debía haber valores de retorno no definidos en Python, en lugar de esto, siempre se lanzarían excepciones cuando un valor de retorno no correcto podía ser computado. Así, los programas escritos en Python nunca fallarían debido a que valores no definidos se estén pasando silenciosamente por detrás. Este es aún un principio importante del lenguaje, tanto en el lenguaje propiamente dicho como en las librerías.

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.

Marzo 12, 2009
» La historia de Python: Microsoft distribuye código Python… en 1996

El siguiente texto es una traducción del artículo Microsoft Ships Python Code… in 1996 de Greg Stein publicado en http://python-history.blogspot.com/.

Microsoft distribuye código Python… en 1996

¡Muchas gracias a Guido por permitirme compartir mi propia historia de Python!

Me guardo mi iniciación en Python para otro post, pero el resultado final fue su introducción en un emprendimiento que co-fundé en 1991 con otras personas. Estábamos trabajando en un gran sistema cliente/servidor de comercio electrónico entre empresas y consumidores. Protocolos TCP propios operando sobre la vieja red X.25 y todo eso. Vieja escuela.

En 1995 nos dimos cuenta, contrariamente a nuestra primera impresión, que la mayoría de los consumidores no estaban en Internet, y que necesitábamos un sistema para nuestros clientes (los vendedores) para que lleguen a sus clientes basados en Internet. Tuve la tarea de definir nuestro enfoque y elegí Python como mi herramienta de prototipado.

Nuestro primer problema fue cambiar a una solución basada totalmente en el navegador. Nuestro cliente a medida ya no era viable, necesitamos una experiencia de compra nueva para los clientes e infraestructura de servidores para soportarla. En ese entonces, hablarle a un navegador significaba escribir scripts de CGI para los servidoes Apache o Netscape HTTP. Usando CGI, me conectaba a nuestro servidor existente para procesar las ordenes, mantener el carrito de compras y obtener información de los productos. Estos scripts producían HTML plano (¡no había AJAX en 1995!).

Este enfoque era menos que ideal ya que cada petición tomaba tiempo y creaba un nuevo proceso CGI. La velocidad de respuesta era muy pobre. Luego, en diciembre de 1995, mientras asistía al Python Workshop en Washington DC, me introdujeron a algunos módulos para Apache y Netscape (de Digital Creations, mejor conocidos como Zope) que corrían en forma persistente en el proceso del servidor. Estos módulos usaban un sistema RPC llamado ILU para comunicarse contra otros procesos por detrás. Con este sistema funcionando, la sobrecarga del CGI desapareció y la experiencia de compra ¡se podía disfrutar bastante! Empezamos a convertir el prototipo en código real. Mientras más lejos íbamos, mejor lucía y más personas se unían al proyecto. El desarrollo se movió muy rápido durante los siguientes meses (¡gracias Python!).

En enero de 1996 Microsoft llamó a nuestra puerta. Su esfuerzo interno por crear un sistema de comercio electrónico no tenía éxito y necesitaban personas que conocieran la industria (nosotros habíamos estado haciendo comercio electrónico ya por varios años en ese momento) y que fueran ágiles. Continuamos desarrollando el software durante la primavera mientras las negociaciones se llevaban a cabo y luego la adquisición finalizó en junio de 1996.

Una vez que llegamos a Microsoft con nuestra pequeña pila de código Pyhon, tuvimos que resolver como distribuir el producto en Windows NT. El equipo al que nos unimos tenía mucha experiencia y creó un plug-in para IIS que permitía comunicarse mediante tuberías nombradas al servidor que estaba por detrás, un servicio de NT con el código de nuestro servidor Python embebido. Con una primavera loca empezando en julio, distribuimos Microsoft Merchant Server 1.0 en octubre de 1996.

Y si… si mirabas bajo la alfombra, en algún lugar escondido, había un intérprete de Python, algunas DLLs y un montón de archivos .pyc. Ciertamente Microsoft no se dio cuenta de este hecho, pero estaba ahí si sabías dónde mirar.
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.

Febrero 24, 2009
» La historia de Python: Historia Personal - parte 2, CNRI y más

El siguiente texto es una traducción del artículo Personal History - part 2, CNRI and beyond de Guido van Rossum publicado en http://python-history.blogspot.com/.

Historia Personal - parte 2, CNRI y más

Luego del workshop de Python (ver artículo anterior) obtuve una oferta de trabajo para venir a trabajar en agentes móviles en el CNRI (Corporation for National Research Initiatives). CNRI es un laboratorio de investigación sin fines de lucro en Reston, Virginia. Me uní en abril de 1995. El director del CNRI, Bob Kahn, fue el primero en señalarme cuánto tenía Python en común con Lisp, a pesar de ser completamente diferentes en un nivel superficial (sintaxis). El trabajo en Python en el CNRI fue financiado indirectamente por un subsidio de DARPA para investigación en agentes móviles. A pesar de que había apoyo de DARPA para proyectos que usaban Python, no había mucho apoyo directo para el desarrollo del lenguaje en si.

En el CRNI, lideré y ayudé a contratar a un pequeño grupo de desarrolladores para construir un agente móvil enteramente en Python. Los miembros iniciales del equipo fueron Roger Masse y Barry Warsaw, quienes habían sido mordidos por el bichito de Python en el Python workshop del NIST.  Además, contratamos a los miembros de la comunidad de Python Ken Manheimer y Fred Drake. Jeremy Hylton, un graduado del MIT contratado originalmente para trabajar en recuperación de texto, también se unió al equipo. Este fue dirigido originalmente por Ted Strollo y luego por Al Vezza.

El equipo me ayudó a crear y mantener elementos adicionales de la infraestructura de la comunidad de Python como el sitio web python.org, el servidor CVS y las listas de correo para varios grupos de intereses especiales en Python. Las versiones de la 1.3 a 1.6 fueron publicados desde el CNRI. Por muchos años, Python 1.5.2 fue la versión más popular y estable.

GNU mailman también nació aquí: originalmente usábamos una herramienta escrita en Perl llamada Majordomo, pero Ken Manheimer encontró que era inmantenible y buscó una solución en Python. Encontró algo escrito en Python por John Viega y tomo su mantenimiento. Cuando Ken dejó el CNRI para ir a Digital Creations, Barry Warsaw lo tomó y convenció a la Free Software Foundation de adoptarlo como su herramienta oficial para listas de correo. Barry entonces la licenció bajo la GPL (GNU Public License).

El Python workshops continuó, al principio dos veces al año, pero debido al crecimiento y los esfuerzos en logística cada vez mayores pronto se convirtió en un evento anual. Estos era llevados a cabo al principio por cualquiera que quería alojarlos, como el NIST (el primero), USGS (el segundo y el tercero) y LLNL (el cuarto y el comienzo de la serie anual). Eventualmente, CNRI tomó la organización, y luego (junto a las conferencias de la WWW y de IETF) se separó como una iniciativa comercial, Fortec. La audiencia pronto llegó a varios miles. Cuando Fortec se desvaneció un poco después de que dejé el CNRI, la Python Conference empezó a desarrollarse dentro de OSCON (O’Reilly’s Open Source Conference), pero al mismo tiempo la Python Software Foundation (ver abajo) empezó una nueva serie de conferencias populares llamada PyCon.

También creamos la primer (hoy inexistente) organización alrededor de Python en el CNRI. En respuesta a los esfuerzos de Mike McLay y Paul Everitt de crear una “Python Foundation”, que terminó en las arenas movedizas de borradores de estatutos, Bob Kahn se ofreció a crear la “Python Software Activity”, que no sería una entidad legal independiente, sino simplemente un grupo de personas trabajando bajo el paraguas legal (sin fines de lucro) del CNRI. La PSA tuvo éxito en congregar la energía de un grupo grande de usuarios de Python comprometidos, pero su falta de independencia limitó su efectividad.

CNRI también usaba dinero de DARPA para financiar el desarrollo de JPython (luego acortado a Jython), una implementación de Python en y para Java. Jim Hugunin creó originalmente JPython mientras hacía su trabajo para graduarse en el MIT. Luego convenció al CNRI de que lo contrate para completar el trabajo (o tal vez el CNRI lo convenció a Jim para que se una –  sucedió mientras estaba de vacaciones). Cuando Jim dejó el CNRI menos de dos años después para unirse al proyecto AspectJ en Xerox PARC, Barry Warsaw continuó el desarrollo de JPython. (Mucho después,  Jim también crearía IronPython, la versión de Python para la plataforma .NET de Microsoft. Jim también escribió la primera versión de Numeric Python).

Otros proyectos en el CNRI también empezaron a usar Python. Muchos de los nuevos desarrolladores principales de Python salieron de allí, en particular Andrew Kuchling, Neil Schemenauer, y Greg Ward, que trabajaron para el proyecto MEMS Exchange. (Andrew contribuyó con Python incluso antes de unirse al CNRI; su primer proyecto grande fue el Python Cryptography Toolkit, una librería de terceros que ponía a disposición de los usuarios de Python muchos de los algoritmos criptográficos fundamentales).

Cuando Python estaba empezando a tener éxito, CNRI intentó encontrar un modelo para financiar su desarrollo más directamente que a través del subsidio de investigación de DARPA. Creamos el Python Consortium, modelado luego del X Consortium, con un costo de inscripción mínimo de u$s 20.000. Sin embargo, excepto por un grupo en Hewlett-Packard, no conseguimos muchos adherentes y eventualmente el consorcio murió de anemia. Otro intento de encontrar fondos fue Computer Programming for Everybody (CP4E), que recibió algún financiamiento de DARPA. Si embargo, el mismo no era suficiente para todo el equipo y resultó que había toda una red de viejos miembros queriendo obtener más dinero del que habíamos obtenido del dinero durante esos años. Eso no fue algo que me agradace, y empecé a buscar otras opciones.

Eventualmente, al principio del 2000, el boom de las las .com, que no había colapsado aún, me convenció a mi y a otros tres miembros del equipo de Python en el CNRI (Barry Warsaw, Jeremy Hylton y Fred Drake) de unirnos a BeOpen.com, una startup en California que estaba reclutando desarrolladores de código abierto. Tim Peters, un miembro clave de la comunidad de Python, también se nos unió.

Anticipándonos a la transición a BeOpen.com, una cuestión difícil fue la propiedad futura de Python. CNRI insistió en cambiar la licencia y pidió que distribuyéramos Python 1.6 con una nueva versión de la misma. La vieja licencia, usada mientras aún estaba en CWI, era una versión de la licencia MIT. Las versiones previas hechas en CNRI usaban una versión ligeramente modificada de esa licencia, básicamente con una sentencia agregada en la que el CNRI se liberaba de la mayoría de las responsabilidades. Sin embargo, la licencia de la versión 1.6 era un montón de palabras en lenguaje técnico escrita por los abogados del CNRI.

Tuvimos varios largos forcejeos con Richard Stallman y Eben Moglen de la Free Software Foundation sobre algunas partes de esta nueva licencia. Se temían que sería incompatible con la GPL, y por lo tanto amenazaba la viabilidad de GNU mailman que se había convertido en una herramienta esencial para la FSF. Con la ayuda de Eric Raymond, se hicieron cambios en la licencia para Python del CNRI que satisficieron tanto a la FSF como al CNRI, pero el lenguaje resultante no es fácil de entender. La única cosa buena que puedo decir sobre esto es que (otra vez gracias a la ayuda de Eric Raymond) tenía el sello de aprobación de la Open Source Initiative como una licencia open source genuina. Solo pequeños cambios se hicieron en el texto de la licencia para reflejar los dos siguientes cambios de propiedad, primero para BeOpen.com y luego para la Python Software Foundation, pero en esencia, el trabajo de los abogados del CNRI todavía perdura.

Como tantas startups de ese entonces, el plan de negocio de BeOpen.com falló espectacularmente. Dejó atrás una gran deuda, algunas serias dudas sobre el rol jugado por algunos administradores de la compañía y algunos muy desilusionados desarrolladores además de mi propio equipo.

Afortunadamente mi equipo, conocido como Python Labs, era bastante reciente y fuimos contratados como una unidad de Digital Creations, una de las primeras compañías en usar Python (Ken Manheimer nos había precedido varios años antes). Digital Creations pronto cambió su nombre a Zope Corporation por su principal producto open source, el sistema web de manejo de contenidos Zope. Los fundadores de Zope, Paul Everitt y Rob Page, asistieron al primer Python workshop en NIST en 1994, como lo hizo su CTO, Jim Fulton.

La historia podría haber sido diferente: además de Digital Creations, también consideramos ofertas de VA Linux and ActiveState. VA Linux era la nueva estrella naciente del mercado de acciones,  pero eventualmente el precio de las mismas (que había hecho multi-millonario a Eric Raymond en los papeles) colapsó más que dramáticamente. Mirando hacia atrás, pienso que ActiveState no hubiera sido una mala elección, a pesar de la controversial personalidad de su fundador Dick Hardt, si no hubiera estado ubicada en Canadá.

En el 2001 creamos la Python Software Foundation, una organización sin fines de lucro, que tuvo como miembros iniciales a los principales desarroladores de Python de ese entonces. Eric Raymond fue uno de los miembros fundadores. Tengo que escribir más sobre este período en otro momento.

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.

Febrero 15, 2009
» Herramientas

Los programadores que usamos Python, no dependemos de herramientas externas al lenguaje en la forma que se depende de ellas cuando se usa un lenguaje como Java.

I can’t tell you how many times I’ve heard people say they wouldn’t use Ruby because it lacks automated refactoring tools. Ruby doesn’t actually need them in the way Java does; it’s like refusing to switch to an electric car because there’s no place to put the gasoline. But programmers are a stubborn bunch, and to win them over you have to give them what they think they want.

http://steve-yegge.blogspot.com/2007/02/next-big-language.html

Enero 26, 2009
» La historia de Python: Historia personal - parte 1, CWI

El siguiente texto es una traducción del artículo Personal History - part 1, CWI de Guido van Rossum publicado en http://python-history.blogspot.com/.

Historia personal - parte 1, CWI

El desarrollo de Python empezó en un instituto de investigación en Amsterdam llamado CWI, el cual es un acrónimo en holandés que en español significa Centro de Matemáticas y Ciencias de la computación. CWI es un lugar interesante; fundado por el Departamento de Educación del gobierno y otros fondos para investigación, guía investigaciones de nivel académico en ciencias de la computación y matemáticas. En todo momento está lleno de estudiantes de doctorado paseando y los más viejos profesionales deben aún recordar su nombre original, el Centro Matemático. Bajo este nombre, es tal vez más famoso por la invención de Algol 68.

Empecé a trabajar en CWI al final de 1982, recién salido de la universidad, como programador en el grupo de desarrollo de ABC liderado por Lambert Meertens y Steven Pemberton. Luego de 4 o 5 años, el proyecto ABC fue interrumpido debido a la obvia falta de éxito y me trasladé al grupo Amoeba liderado por Sape Mullender. Amoeba era un sistema operativo distribuido basado en micro-kernel desarrollado en forma conjunta por CWI y la Vrije Universiteit of Amsterdam, bajo la dirección de Andrew Tanenbaum. En 1991, Sape dejó CWI para dar clases en la University of Twente y terminé en otro grupo recientemente formado en CWI sobre multimedia liderado por Dick Bulterman.

Python es un producto directo de mi experiencia en CWI. Como explico más adelante, ABC me dio la inspiración clave para Python, Amoeba la motivación inmediata, y el grupo multimedia fomentó su crecimiento. Sin embargo, hasta donde sé, ningún fondo de CWI fue alguna vez destinado oficialmente para su desarrollo. En cambio, sólo evolucionó como una herramienta importante para usar en los grupos Amoeba y de multimedia.

Mi motivación original pare crear Python fue la necesidad que percibí por un lenguaje de alto nivel en el proyecto Amoeba. Me dí cuenta de que el desarrollo de utilidades para administración de sistemas en C llevaría mucho tiempo. Más aún, hacer esto en el shell Bourne funcionaría por una variedad de razones. La más importante fue que al ser Amoeba un sistema micro-kernel distribuido con un diseño radicalmente nuevo, sus operaciones primitivas eran diferentes (y de granularidad fina) de las primitivas tradicionales disponibles en el shell Bourne. Existía la necesidad de un nuevo lenguaje que “una la brecha entre C y el shell”. Por mucho tiempo, este fue el principal slogan de Python.

En este punto tal vez se pregunte “¿por qué no portar un lenguaje existente?”. En mi forma de verlo, no había muchos lenguajes apropiados en esos días. Estaba familiarizado con Perl 3, pero estaba más atado a Unix que el shell Bourne. Tampoco me gustaba la sintaxis de Perl –mi gusto en la sintaxis de los lenguajes de programación fue fuertemente influenciado por lenguajes como Algol 60, Pascal, Algol 68 (a todos los había aprendido mucho antes) y, por último pero no el peor, ABC, en el cual gasté cuatro años de mi vida. Así que decidí diseñar un lenguaje por mi cuenta que tomaría prestado todo lo que me gustaba de ABC a la vez que arreglaba todos sus problemas (como los percibía).

¡El primer problema que decidí arreglar era el nombre! Sucedió que el equipo de desarrollo de ABC tenía algunos problemas en elegir un nombre para su lenguaje. El nombre original del lenguaje, B, tuvo que ser abandonado porque se confundía con otro lenguaje llamado B, que era más viejo y más conocido. De cualquier modo, B era solo un título de trabajo (el chiste era que B era el nombre de la variable que contenía el nombre del lenguaje, de ahí la itálica). El equipo tuvo un concurso público para obtener un nuevo nombre, pero ninguna de las propuestas fue apropiada, y al final el backup interno prevaleció. Con el nombre se quería expresar la idea de que el lenguaje hacía que la programación sea “tan simple como el ABC”, pero a mi nunca me convenció mucho.

Así que, en lugar de sobre analizar el problema del nombre, decidí hacer lo contrario. Elegí el primer nombre que me vino a la mente, que resultó ser Monty Python’s Flying Circus, uno de mis grupos cómicos preferidos. La referencia parece bastante irrelevante para lo que era esencialmente un proyecto innovador pero solitario. La palabra “Python” era pegadiza, te ponía un poco los pelos de punta, y al mismo tiempo encajaba en la tradición de ponerle a los lenguajes nombres de personas famosas, como Pascal, Ada y Eiffel. Puede que el equipo de The Monty Python no sea famoso por sus avances en ciencia o tecnología, pero son ciertamente un favorito de los geeks. También encajaba en la tradición del grupo Amoeba de CWI de ponerle a los programas el nombre de shows televisivos.

Por muchos años resistí la tentación de asociar el lenguaje con serpientes. Finalmente me rendí cuando O’Reilly quizo poner una serpiente en la tapa del primer libro de Python “Programming Python”. Era una tradición de O’Reilly usar fotos de animales, y si tenía que ser un animal, que sea una serpiente.

Con el asunto del nombre resuelto, empecé a trabajar en Python a finales de diciembre de 1989, y tuve una versión funcionando en los primeros meses de 1990. No tomé notas, pero recuerdo vivamente que la primer pieza de código que escribí de la implementación de Python era un LL(1) parser generator simple que llamé “pgen”. Este parser generator es aún parte de los fuentes de Python y probablemente lo menos cambiado de todo el código. Esta versión temprana de Python fue usada por algunas personas en CWI, mayormente, pero no en forma exclusiva en el grupo Amoeba durante 1990. Los desarrolladores clave, demás de mi, eran mis compañeros de oficina, los programadores Sjoerd Mullender (el hermano menor de Sape) y Jack Jansen (quien fue uno de los desarrolladores líderes de la versión para Macintosh por muchos años, luego de dejar CWI).

El 20 de febrero de 1991 publiqué por primera vez Python para el mundo en el grupo de noticias alt.sources (como 21 partes codificadas que tenían que ser juntadas y decodificadas para formar el archivo tar comprimido). Esta versión fue etiquetada 0.9.0 y publicada bajo una licencia que era prácticamente una copia textual de la licencia MIT usada por el proyecto X11 en ese entonces, poniendo “Stichting Mathematisch Centrum”, la organización padre de CWI, como la entidad responsable legal. Así que, como casi todo lo que he escrito, Python era open source antes de que el término sea inventado por Eric Raymond y Bruce Perens a finales de 1997.

Enseguida hubo mucha retroalimentación y con este apoyo mantuve un firme flujo de publicaciones durante algunos años. Empecé a usar CVS para seguir los cambios y para facilitar compartir la responsabilidad de codificar con Sjoerd y Jack (coincidentemente, CVS fue desarrollado originalmente como un set de shell scripts por Dick Grune, quien era uno de los miembros originales del equipo de desarrollo de ABC). Escribí una FAQ, que era regularmente publicada en algunos grupos de noticias, como era costumbre para las FAQs en aquellos días anteriores a la web. Empecé una lista de correos, en marzo de 1993 se creó el grupo de noticias comp.lang.python con mi apoyo pero sin estar directamente involucrado. El grupo de noticias y la lista de correos fueron unidos mediante un gateway bidireccional que aún existe, aunque hoy está implementado con mailman (el gestor de listas de correos líder, escrito en Python).

En el veranode 1994, en el grupo de noticias apareció un hilo titulado “¿si a Guido lo atropella un colectivo?” sobre la dependencia de la creciente comunidad de Python en mis contribuciones personales. Culminó con una invitación que me hizo Michael McLay para pasar dos meses como investigador invitado en el NIST, el Instituto Nacional de Estándares y Tecnologías de los Estados Unidos, antes el Bureau Nacional de Estándares, en Gaithersburg, Maryland. Michael tenía varios “clientes” del NIST interesados en usar Python para una variedad de proyectos relacionados con estándares y el presupuesto para mi estancia allí nació de la necesidad de ayudarlos a mejorar sus habilidades con Python, así como posiblemente adaptar Python a sus necesidades.

El primer workshop de Python se llevó a cabo mientras estuve allí en noviembre de 1994, con el programador del NIST, Ken Manheimer, dándome un importante apoyo. De los aproximadamente 20 asistentes, alrededor de la mitad son aún participantes activos de la comunidad de Python y algunos se volvieron líderes principales de proyectos open sources (Jim Fulton de Zope y Barry Warsaw de GNU mailman). Con el apoyo del NIST también di una charla para 400 personas en la conferencia Usenix Little Languages en Santa Fe, organizada por Tom Christiansen, un defensor de Perl de mente abierta quien me presentó al creador de Perl, Larry Wall, y al author de Tcl/Tk, John Ousterhout.

En la próxima entrega: cómo conseguí un trabajo en los Estados Unidos…
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.

Enero 21, 2009
» La historia de Python: Cronología breve

El siguiente texto es una traducción del artículo A Brief Timeline of Python de Guido van Rossum publicado en http://python-history.blogspot.com/.

Cronología breve de Python

El desarrollo de Python y el de otros lenguajes de programación dinámicos (y open source) como Tcl, Perl y (más tarde) Ruby se llevaron a cabo al mismo tiempo; estos se desarrollaban activamente a la vez que iban ganando popularidad. Para poner a Python en su propia perspectiva histórica, la siguiente lista muestra su historia de releases (publicaciones). Las fechas más tempranas son aproximadas, ya que no llevaba un registros de todos los eventos:

Fecha del release
Versión
Diciembre de 1989 Empieza la implementación
1990 Release interno en CWI
20 de febrero de 1991 0.9.0 (release 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 (solo para Macintosh)
6 de abril 1992 0.9.6
Algún momento de 1992 0.9.7beta
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
20 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

Añadí enlaces a los releases que aún se anuncian en python.org en estos momentos. Notar que muchos releases fueron seguidos de varios micro releases, por ejemplo 2.0.1; no me molesté en incluirlos en la tabla porque se habría vuelto muy larga. El código fuente empaquetado de cada viejo release está disponible en http://www.python.org/ftp/python/src/. Varios releases antiguos en forma binaria y otros artefactos históricos también pueden encontrarse subiendo un nivel desde allí.
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.

» La historia de Python: Filosofía de diseño

El siguiente texto es una traducción del artículo Python’s Design Philosophy de Guido van Rossum publicado en http://python-history.blogspot.com/.

Filosofía de Diseño de Python

Los próximos artículos se sumergirán en los detalles internos de la historia de Python. Sin embargo, antes de hacer eso, me gustaría detallar las guías filosóficas que me ayudaron a tomar decisiones mientras diseñaba e implementaba Python.

Ante todo, Python fue originalmente concebido como un proyecto de una sola persona; no había presupuesto oficial, y quería resultados rápidos, en parte para poder convencer a los gerentes de apoyar el proyecto (en el que estaba teniendo bastante éxito). Esto llevó a un número de reglas para ahorrar tiempo:

  • Tomar ideas prestadas de otros lugares siempre que tuviera sentido.
  • “Las cosas deben ser tan simples como sea posible, pero no más simples.” (Einstein)
  • Haz una cosa bien (la “filosofía de Unix”).
  • No preocuparse mucho sobre la performance; planea optimizar luego, cuando sea necesario.
  • No pelear con el entorno y seguir la corriente.
  • No intentar la perfección porque “suficientemente bueno” es a menudo eso.
  • (Corolario) Está bien tomar atajos a veces, especialmente si se puede hacer bien luego.

Otros principios no fueron pensados para ahorrar tiempo. A veces era justamente lo contrario:

  • La implementación de Python no debe estar atada a una plataforma en particular. Está bien si algunas funcionalidades no están siempre disponibles, pero el núcleo debe correr en todo lugar.
  • No molestar a los usuarios con detalles de los que la máquina se puede encargar (no siempre seguí esta regla y algunas de las desastrosas consecuencias se describen en las siguientes secciones).
  • Soportar y animar a que el código de los usuarios sea independiente de la plataforma, pero no evitar el acceso a las capacidades o propiedades de la plataforma (esto contrasta bruscamente con Java).
  • Un sistema complejo y grande debe poderse extender en muchos niveles. Esto maximiza las oportunidades para los usuarios, sofisticados o no, de ayudarse a sí mismos.
  • Los errores no deben ser fatales. Esto significa que el código de los usuarios debe ser capaz de recuperarse de condiciones de error mientras que la máquina virtual siga funcionando.
  • Al mismo tiempo, los errores no deben pasar inadvertidos (estos dos últimos ítems llevan naturalmente a la decisión de usar excepciones en la implementación).
  • No se debe permitir que un error en el código Python del usuario lleve al intérprete de Python a un comportamiento no definido; un fallo de segmento (core dump) no es nunca culpa del usuario.

Finalmente, tengo varias ideas sobre el diseño de buenos lenguajes de programación, que el grupo de desarrollo de ABC marcó en mí; en él tuve mi primera experiencia real con el diseño e implementación de lenguajes. Estas ideas son las más difíciles de poner en palabras, ya que la mayoría giran en torno a conceptos subjetivos como elegancia, simplicidad y legibilidad.

A pesar de que más adelante voy a tratar más sobre la influencia de ABC en Python, me gustaría mencionar especialmente una regla de legibilidad: los caracteres de puntuación deben ser usados en forma conservadora, de forma alineada con el uso que se les da en Inglés o álgebra universitaria. Se hacen excepciones cuando una notación particular es una tradición antigua en los lenguajes de programación, como “x*y” para multiplicar, “a[i]” para acceder al elemento de un array o “x.foo” para seleccionar un atributo, pero Python no usa “$” para indicar variables, ni “!” para indicar operaciones con efectos secundarios.

Tim Peters, un antiguo usuario de Python quien eventualmente se convirtió en su más prolífero y tenaz desarrollador del núcleo, intentó capturar mis principios de diseño tácitos en lo que llamó el “Zen de Python“. Lo cito aquí enteramente:

  • Hermoso es mejor que feo.
  • Explícito es mejor que implícito.
  • Simple es mejor que complejo.
  • Complejo es mejor que complicado.
  • Plano es mejor que anidado.
  • Ralo es mejor que denso.
  • La legibilidad importa.
  • Los casos especiales no son tan especiales como para romper las reglas.
  • Aunque lo práctico vence a lo puritano.
  • Los errores nunca deben pasar desapercibidos.
  • A menos que sean explícitamente silenciados.
  • Ante la ambigüedad, rechaza la tentación de adivinar.
  • Debe haber una forma (y preferentemente sólo una forma) obvia de hacerlo.
  • Aunque esa forma no sea obvia al principio a menos que seas Holandés.
  • Ahora es mejor que nunca.
  • Aunque nunca es a menudo mejor que ya.
  • Si la implementación es difícil de explicar, es una mala idea.
  • Si la implementación es fácil de explicar, puede ser una buena idea.
  • Los espacios de nombre son geniales; hagamos más de ellos!

A pesar de que mi experiencia en el grupo de desarrollo de ABC influenció en gran medida a Python, tenían algunos principios de diseño que eran radicalmente diferentes a los de Python. De muchas maneras, Python nace deliberadamente de estos:

  • El grupo de desarrollo de ABC se esforzaba en alcanzar la perfección. Por ejemplo, usaban algoritmos de estructuras de datos arbóreas que se había probado eran óptimos para casi infinitas colecciones (pero no eran tan geniales para colecciones pequeñas).
  • Querían aislar al usuario, tanto como sea posible, del “enorme y malvado mundo de las computadoras” que había ahí afuera. No solo no debía haber un límite en el rango de números, el largo de las cadenas de texto, o el tamaño de las colecciones (aparte del total de memoria disponible), sino que los usuarios tampoco tendrían que ocuparse de discos, otros programas o guardar archivos. ABC debía ser la única herramienta que podrían necesitar. Este deseo también causó que el grupo de desarrollo de ABC cree un entorno de edición totalmente integrado, único para ABC (por supuesto, había una posibilidad de escapar del entorno de ABC, pero fue una idea de último momento y no era accesible directamente desde el lenguaje).
  • Asumieron que los usuarios no tendrían experiencia previa con computadoras (o estarían deseando olvidarla). Así, se introdujo terminología alternativa considerada más amigable para los novatos que términos estándares de programación. Por ejemplo, los procedimientos se llamaban “how-tos” y las variables “locations”.
  • El grupo de desarrollo de ABC diseñó ABC sin un camino evolutivo en mente y sin esperar la participación de los usuarios en el diseño del lenguaje. ABC fue creado como un sistema cerrado, tan impecable como sus diseñadores podían hacerlo. No se alentaba a los usuarios a mirar que pasaba dentro. Aunque se habló sobre abrir partes de la implementación a usuarios avanzados durante las últimas etapas del proyecto, esto nunca se concretó.

En muchas formas, la filosofía de diseño que usé cuando diseñaba Python es probablemente una de las principales razones de su éxito final. En lugar de intentar alcanzar la perfección, los primeros usuarios vieron que Python funcionaba “suficientemente bien” para sus propósitos. Al crecer la base de usuarios, se incorporaron gradualmente sugerencias para mejorar el lenguaje. Como veremos en las futuras secciones, muchas de estas mejoras necesitaron cambios sustanciales y la reimplementación del núcleo del lenguaje. Incluso hoy, Python continua evolucionando.

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.

Enero 20, 2009
» Asociación de métodos en tiempo de ejecución en Python

En el primer artículo de La Historia de Python (en) se menciona, entre las cualidades que hacen a Python un lenguaje que permite la programación orientada a objetos, la posibilidad de “asociación de métodos en tiempo de ejecución” (”run-time binding of methods”). Hoy en otro artículo del autor, vuelvo a leer sobre el tema:

Now instances of C have a method with one argument named ‘meth’ that works exactly as before. It even works for instances of C that were created before the method was poked into the class.

Vamos a ver un ejemplo de esto:

>>> class C:
...     pass
...
>>> c1 = C()
>>> c1.hello()
Traceback (most recent call last):
  File "", line 1, in
AttributeError: C instance has no attribute 'hello'
>>> def hello(myself, name):
...     myself.lasthello = name
...     print "Hello %s" % name
...
>>> C.hello = hello
>>> c2 = C()
>>> c2.hello("mary")
Hello mary
>>> c1.hello("juanjo")
Hello juanjo

» La historia de Python: Introducción y repaso

El siguiente texto es una traducción del artículo Introduction and Overview de Guido van Rossum publicado en http://python-history.blogspot.com/. Mi objetivo es traducir todos los artículos que se publiquen en ese blog.

Introducción

Python es en la actualidad uno de los lenguajes de programación dinámicos más populares, junto con Perl, Tcl, PHP y ahora Ruby. A pesar de ser a menudo visto como un lenguaje de “scripting”, es en realidad un lenguaje de programación de propósito general como Lisp o Smalltalk. Hoy, Python es usado para todo, desde scripts que luego se tiran hasta servidores web de alta escalabilidad que proveen servicio ininterrumpido 24×7. Permite crear interfaces de usuario, programar con bases de datos, realizar programación web tanto del lado del servidor como del cliente y testear aplicaciones. Es usado por científicos que escriben aplicaciones para las más rápidas supercomputadoras del mundo y por niños que están aprendiendo a programar.

En este blog, voy a concentrarme en la historia de Python. En particular, cómo Python fue desarrollado, las principales influencias en su diseño, errores cometidos, lecciones aprendidas y futuras direcciones para el lenguaje.

Reconocimiento: Estoy en deuda con Dave Beazley por muchas de las mejores oraciones en este blog. (Para saber más sobre los orígenes de este blog, vea mi otro blog.)

Un repaso rápido a Python

Cuando uno se encuentra con Python por primera vez, a menudo se sorprende de que el código Python se vea, por lo menos superficialmente, similar a código escrito en otros lenguajes de programación convencionales como C o Pascal. Esto no es un accidente; la sintaxis de Python toma mucho prestado de C. Por ejemplo, muchas de las palabras claves de Python (if, else, while, for, etc.) son las mismas que en C, los identificadores en Python tienen las mismas reglas que los de C, y muchos de los operadores estándares tienen el mismo significado que en C. Por supuesto, Python no es C y una de las principales áreas dónde difiere es que en lugar de usar llaves para agrupar sentencias, usa indentación. Por ejemplo, en lugar de escribir sentencias en C como:

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

Python simplemente se deshace de todas las llaves (y de los punto y coma finales) y usa la siguiente estructura

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

Otra área importante en la que Python se diferencia de los lenguajes del estilo de C es en su uso de tipado dinámico. En C, las variables siempre deben ser declaradas explícitamente y se les debe dar un tipo específico como int o double. Esta información es luego usada para hacer verificaciones estáticas en tiempo de compilación del programa y para reservar memoria para almacenar el valor de las variables. En Python, las variables son simplemente nombres que hacen referencia a objetos. No se necesita declarar las variables antes de que sean asignadas e incluso pueden cambiar de tipo en el medio de un programa. Como otros lenguajes de programación dinámicos, todas las verificaciones de tipo se hacen en tiempo de ejecución por un intérprete en lugar de en un paso de compilación separado.

Los tipos de datos primitivos que vienen con Python incluyen booleanos, números (enteros de máquina, enteros de precisión arbitraria, y números de coma flotante reales y complejos), y cadenas de texto (de 8 bits y Unicode). Estos son todos tipos de datos inmutables, esto significa que los valores son representados por objetos que no pueden ser modificados después de su creación. Los tipos de datos compuestos que viene con el lenguaje incluyen tuplas (arrays inmutables), listas (arrays que pueden cambiar de tamaño) y diccionarios (tablas hash).

Para organizar los programas, Python soporta paquetes (grupos de módulos y/o paquetes), módulos (código relacionado agrupado en un solo archivo fuente), clases, métodos o funciones. Para control de flujo, provee if/else, while y una sentencia for de alto nivel que itera sobre cualquier objeto “iterable”. Para el manejo de errores, Python usa excepciones. Una sentencia raise lanza una excepción, y las sentencias try/except/finally especifican manejadores para las excepciones. Las operaciones que vienen con el lenguaje lanzan excepciones cuando se encuentran condiciones de error.

En Python, todos los objetos que pueden ser nombrados son llamados “de primera clase”. Esto significa que funciones, clases, métodos, módulos y todos los otros objetos nombrados pueden ser libremente pasados como parámetros, inspeccionados y almacenados en distintas estructuras de datos (por ejemplo, listas o diccionarios) en tiempo de ejecución. Y hablando de objetos, Python también tiene soporte completo para programación orientada a objetos; incluyendo clases definidas por el usuario, herencia y asociación de métodos en tiempo de ejecución.

Python tiene una extensa librería estándar, la cual es una de las razones de su popularidad. La librería estándar tiene más de 100 módulos y siempre está evolucionando. Alguno de estos módulos incluyen expresiones regulares, funciones matemáticas típicas, hilos, interfaces con sistemas operativos, redes, protocolos estándares de Internet (HTTP, FTP, SMTP, etc.), manejo de email, procesamiento de XML, procesamiento de HTML y toolkits para GUI (Tcl/Tk).

Además, hay una gran cantidad de módulos y paquetes provistos por terceros, muchos de los cuales también son open source. Aquí uno puede encontrar frameworks web (¡muchos como para listarlos!), más toolkits para GUI, librerías numéricas eficientes (incluyendo wrappers para muchos paquetes populares escritos en Fortran), interfaces con bases de datos relacionales (Oracle, MySQL y otras), SWIG (una herramienta para hacer que librerías C++ estén disponibles como módulos Python), y mucho más.

Un atractivo principal de Python (y de otros lenguajes de programación dinámicos en ese aspecto) es que tareas que parecen complicadas a menudo pueden ser expresadas con muy poco código. Por ejemplo, aquí hay un script en Python simple que obtiene una página web, la revisa buscando referencias a URLs, e imprime las primeras 10.

# 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 hacer un web crawler, y en efecto Scott Hassan me ha dicho que escribió el primer web crawler de Google en Python. Hoy, Google emplea millones de líneas de código Python para manejar muchos aspectos de sus operaciones, desde automatización a manejo de avisos (Descargo: en la actualidad soy un empleado de Google.)

Por debajo, Python es implementado como una combinación de intérprete y compilador a bytecode. La compilación es llevada a cabo implícitamente cuando los módulos son cargados, y muchas primitivas del lenguaje necesitan que el compilador esté disponible en tiempo de ejecución. A pesar de que la implementación de referencia de Python está escrita en C, y está disponible para cada plataforma de hardware/software existente, muchas otras implementaciones se han vuelto populares. Jython es una versión que corre sobre la JVM y tiene integración con Java. IronPython es una versión para la plataforma .NET de Microsoft que tiene una integración similar con otros lenguajes que corren sobre la plataforma .NET. PyPy es un compilador/intérprete de Python escrito en Python (aún un proyecto de investigación, financiado con fondos de la UE). También está Stackless Python, una variante de la implementación en C que reduce la dependencia del stack de C para llamadas a funciones/métodos, para permitir co-rutinas, continuations y micro hilos.

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.

Enero 12, 2009
» Por qué nos gusta Python

El siguiente texto era parte del informe del proyecto final de carrera de Juan José Conti y Cecilia Lorena Puccinelli, titulado Desarrollo Ágil de un Sistema de Gestión de Negocios Inmobiliarios con Software Libre. Finalmente no fue incluido en la versión definitiva del mismo por lo que lo rescatamos en este blog.

Python

Python es un lenguaje de programación de propósito general de alto nivel. Su filosofía de diseño hace énfasis en lograr productividad para los programadores y legibilidad del código.

Fue creado en el año 1990 por Guido Van Rossum y desde entonces ha sido desarrollado como un proyecto del Software Libre por desarrolladores alrededor del mundo.

A menudo es comparado con lenguajes como C, Java, Perl y Lisp, pero tiene muchas características distintivas que marcan su identidad propia:

Intérprete interactivo

Python viene acompañado de un intérprete interactivo o read-eval-print-loop que permite agilizar el desarrollo de programas ya que se pueden probar ideas y rápidamete ver como resultan sin necesidad de escribir un programa entero en el cual envolverla. Otros lenguajes como Smalltalk, Haskell o Scheme también tienen esta herramienta.

El siguiente es un ejemplo sencillo de uso del intérprete interactivo, los comandos ingresados por el usuario están precedidos por >>>:

>>> 1 + 2
3
>>> "hola"
'hola'
>>> nombre = "fabio"
>>> "hola " + nombre
'hola fabio'

Tipos de datos poderosos

Python incluye de forma nativa tipos de datos poderosos muy útiles a la hora de programar. Las listas y tuplas son secuencias de objetos:

lista = [1,2,3, “cuatro”, Cliente()]
tupla = ('u', 1, 3)

Ambos tipos de datos pueden almacenar cualquier tipo de objetos en orden. La principal diferencia entre estos dos tipos de datos es que las listas pueden cambiar en forma dinámica su tamaño mientras que las tuplas son de tamaño fijo (se dice que es un objeto inmutable, como los números o cadenas de texto).

Los diccionarios son una estructura de datos sin orden que permite almacenar valores asociados a una clave para luego poder recuperarlos a partir de esta. Solo objetos inmutables pueden ser clave de un diccionario.

dicc = {'a': 1, 'b': 10}
dicc['a'] obtiene el objeto 1
dicc[3] = ‘tres’ agrega al diccionario el objeto ‘tres’ asociado a la clave 3 .

Espacios en blanco con significado

En Python los bloques se delimitan mediante el uso de indentación en lugar de utilizar llaves:

if carteles == 0:
    print “No hay carteles disponibles.”
else:
    print “Hay %d carteles disponibles.” % carteles

Esto tiene como objetivo forzar una correcta indentación que redunde en una mayor legibilidad del código escrito.

i = 20
while i > 0:
    print i
    i -= 1

Tipado de pato

El nombre tipado de pato surge de la idea: “si un objeto camina como pato y come como pato, entonces debe ser un pato”.

Las restricciones de tipo no son checkeadas en tiempo de compilación, sino que las operaciones sobre los objetos siempre se intentan llevar a cabo y fallan en tiempo de ejecución si el objeto no puede responder al mensaje que se le envió.

Supongamos que definimos la función saludar como sigue:

def saludar(o):
    print “hola %s“ % o.nombre

A esta función no lo interesa el tipo del objeto o, recibido como parámetro, lo único que le importa es que tenga el atributo nombre.

Así, si la variable m referencia un objeto de la clase Mujer y la variable h referencia un objeto de la clase Hombre y ambas clases tienen un atributo llamado nombre, entonces las siguientes llamadas se ejecutarán sin problemas:

saludar(m)
saludar(h)

Si la variable ‘a’ referencia a un objeto de la clase Anonimo y ésta no tiene un atributo llamado nombre, la llamada a:

saludar(a) lanzará una excepción.

Librería estándar amplia

Python viene con las baerías incluidas!

Python viene con las baerías incluidas!

Al igual que Java, Python cuenta con una amplia librería estándar que acompaña al lenguaje. Ésta incluye módulos para manejar expresiones regulares, crear interfaces gráficas, conectarse a bases de datos entre muchos otros.

Ésta es una de sus mayores ventajas y a esto se debe la popular expresión “Python viene con las baterías incluidas”.

Librerías externas

Además de los muchos componentes incluidos en la librería estándar de Python, hemos utilizando algunas librerías externas:

  • PIL: librería para manejo de imágenes.
  • Pysicopg: conector para el motor de bases de datos PostgreSQL.
  • ReportLab: librería para generar documentos PDF.
  • BeautifulSoap: librería para procesar documentos HTML. Su uso es descrito en la sección 1.1.37.
  • Pynum2word: módulo que convierte números en palabras.