Novedades de Python 2.0

Autor

A.M. Kuchling y Moshe Zadka

Introducción

El 16 de octubre de 2000 se publicó una nueva versión de Python, la 2.0. Este artículo cubre las emocionantes nuevas características de la 2.0, destaca algunos otros cambios útiles y señala algunos cambios incompatibles que pueden requerir reescribir el código.

El desarrollo de Python nunca se detiene por completo entre versiones, y siempre se presenta un flujo constante de correcciones de errores y mejoras. Una gran cantidad de correcciones menores, algunas optimizaciones, docstrings adicionales y mejores mensajes de error fueron incluidos en la versión 2.0; sería imposible enumerarlos todos, pero son ciertamente significativos. Consulte los registros CVS disponibles públicamente si desea ver la lista completa. Este progreso se debe a que los cinco desarrolladores que trabajan para PythonLabs reciben ahora una remuneración por pasar sus días arreglando errores, y también a la mejora de la comunicación resultante del traslado a SourceForge.

¿Qué pasa con Python 1.6?

Python 1.6 puede considerarse como la versión de Python de las Obligaciones Contractuales. Después de que el equipo principal de desarrollo dejara el CNRI en mayo de 2000, el CNRI pidió que se creara una versión 1.6 que contuviera todo el trabajo sobre Python que se había realizado en el CNRI. Por lo tanto, Python 1.6 representa el estado del árbol CVS en mayo de 2000, siendo la novedad más importante el soporte de Unicode. El desarrollo continuó después de mayo, por supuesto, así que el árbol 1.6 recibió algunas correcciones para asegurar que es compatible con Python 2.0. 1.Por lo tanto, la 6 es parte de la evolución de Python, y no una rama lateral.

So, should you take much interest in Python 1.6? Probably not. The 1.6final and 2.0beta1 releases were made on the same day (September 5, 2000), the plan being to finalize Python 2.0 within a month or so. If you have applications to maintain, there seems little point in breaking things by moving to 1.6, fixing them, and then having another round of breakage within a month by moving to 2.0; you’re better off just going straight to 2.0. Most of the really interesting features described in this document are only in 2.0, because a lot of work was done between May and September.

Nuevo proceso de desarrollo

El cambio más importante en Python 2.0 puede que no sea en el código en absoluto, sino en la forma de desarrollar Python: en mayo de 2000 los desarrolladores de Python comenzaron a utilizar las herramientas puestas a disposición por SourceForge para almacenar el código fuente, rastrear los informes de errores y gestionar la cola de envíos de parches. Para informar de errores o enviar parches para Python 2.0, utilice las herramientas de seguimiento de errores y gestión de parches disponibles en la página del proyecto Python, situada en https://sourceforge.net/projects/python/.

El más importante de los servicios alojados ahora en SourceForge es el árbol CVS de Python, el repositorio de versiones controladas que contiene el código fuente de Python. Anteriormente, había unas 7 personas que tenían acceso de escritura al árbol CVS, y todos los parches tenían que ser inspeccionados y comprobados por una de las personas de esta corta lista. Obviamente, esto no era muy escalable. Al trasladar el árbol CVS a SourceForge, fue posible conceder acceso de escritura a más personas; en septiembre de 2000 había 27 personas que podían revisar los cambios, un aumento de cuatro veces. Esto hace posible cambios a gran escala que no se intentarían si tuvieran que pasar por el pequeño grupo de desarrolladores del núcleo. Por ejemplo, un día a Peter Schneider-Kamp se le ocurrió dejar de lado la compatibilidad con K&R C y convertir el código fuente de Python a ANSI C. Después de obtener la aprobación en la lista de correo de python-dev, se lanzó a una ráfaga de revisiones que duró aproximadamente una semana, otros desarrolladores se unieron para ayudar, y el trabajo estaba hecho. Si sólo hubiera habido 5 personas con acceso de escritura, probablemente esa tarea habría sido considerada como «agradable, pero no vale la pena el tiempo y el esfuerzo necesarios» y nunca se habría realizado.

El cambio al uso de los servicios de SourceForge ha dado lugar a un notable aumento de la velocidad de desarrollo. Ahora los parches se envían, se comentan, son revisados por otras personas además del remitente original, y van de un lado a otro hasta que se considera que el parche merece ser revisado. Los errores se rastrean en una ubicación central y se pueden asignar a una persona específica para que los corrija, y podemos contar el número de errores abiertos para medir el progreso. Esto no ha tenido un coste: los desarrolladores tienen ahora más correo electrónico con el que lidiar, más listas de correo que seguir, y se han tenido que escribir herramientas especiales para el nuevo entorno. Por ejemplo, SourceForge envía por defecto mensajes de correo electrónico de notificación de parches y errores que son completamente inútiles, por lo que Ka-Ping Yee escribió un raspador de pantalla HTML que envía mensajes más útiles.

The ease of adding code caused a few initial growing pains, such as code was checked in before it was ready or without getting clear agreement from the developer group. The approval process that has emerged is somewhat similar to that used by the Apache group. Developers can vote +1, +0, -0, or -1 on a patch; +1 and -1 denote acceptance or rejection, while +0 and -0 mean the developer is mostly indifferent to the change, though with a slight positive or negative slant. The most significant change from the Apache model is that the voting is essentially advisory, letting Guido van Rossum, who has Benevolent Dictator For Life status, know what the general opinion is. He can still ignore the result of a vote, and approve or reject a change even if the community disagrees with him.

Producir un parche real es el último paso en la adición de una nueva característica, y suele ser fácil en comparación con la tarea anterior de llegar a un buen diseño. Las discusiones sobre nuevas funcionalidades a menudo pueden explotar en largos hilos de la lista de correo, haciendo que la discusión sea difícil de seguir, y nadie puede leer todos los mensajes en python-dev. Por lo tanto, se ha establecido un proceso relativamente formal para escribir Propuestas de Mejora de Python (PEPs), siguiendo el modelo del proceso RFC de Internet. Las PEP son borradores de documentos que describen una nueva característica propuesta, y se revisan continuamente hasta que la comunidad llega a un consenso, aceptando o rechazando la propuesta. Cita de la introducción de PEP 1, «PEP Purpose and Guidelines»:

PEP son las siglas de Python Enhancement Proposal. Un PEP es un documento de diseño que proporciona información a la comunidad de Python, o que describe una nueva característica para Python. El PEP debe proporcionar una especificación técnica concisa de la característica y una justificación de la misma.

Pretendemos que los PEPs sean los mecanismos principales para proponer nuevas características, para recoger las opiniones de la comunidad sobre un tema y para documentar las decisiones de diseño que se han tomado en Python. El autor del PEP es responsable de crear consenso dentro de la comunidad y de documentar las opiniones discrepantes.

Lee el resto de PEP 1 para conocer los detalles del proceso editorial, el estilo y el formato de los PEP. Los PEP se mantienen en el árbol CVS de Python en SourceForge, aunque no forman parte de la distribución de Python 2.0, y también están disponibles en formato HTML en https://www.python.org/dev/peps/. En septiembre de 2000, había 25 PEPS, desde PEP 201, «Lockstep Iteration», hasta PEP 225, «Elementwise/Objectwise Operators».

Unicode

La mayor novedad de Python 2.0 es un nuevo tipo de datos fundamental: Las cadenas Unicode. Unicode utiliza números de 16 bits para representar los caracteres en lugar de los 8 bits utilizados por ASCII, lo que significa que se pueden admitir 65.536 caracteres distintos.

La interfaz final para el soporte de Unicode se alcanzó a través de innumerables discusiones, a menudo tormentosas, en la lista de correo de python-dev, y fue implementada en su mayor parte por Marc-André Lemburg, basándose en una implementación del tipo de cadena Unicode de Fredrik Lundh. Una explicación detallada de la interfaz fue escrita como PEP 100, «Python Unicode Integration». Este artículo se limitará a cubrir los puntos más significativos de las interfaces Unicode.

En el código fuente de Python, las cadenas Unicode se escriben como u "cadena". Los caracteres Unicode arbitrarios pueden escribirse utilizando una nueva secuencia de escape, uHHHH, donde HHHH es un número hexadecimal de 4 dígitos desde 0000 hasta FFFF. También se puede utilizar la secuencia de escape existente xHHHH, y se pueden utilizar escapes octales para caracteres hasta U+01FF, que se representa con 777.

Las cadenas Unicode, al igual que las cadenas normales, son un tipo de secuencia inmutable. Pueden ser indexadas y cortadas, pero no modificadas en su lugar. Las cadenas Unicode tienen un método encode( [encoding] ) que retorna una cadena de 8 bits en la codificación deseada. Las codificaciones son nombradas por cadenas, como 'ascii', 'utf-8', 'iso-8859-1', o lo que sea. Se define una API de códecs para implementar y registrar nuevas codificaciones que luego están disponibles en todo el programa Python. Si no se especifica una codificación, la codificación por defecto suele ser ASCII de 7 bits, aunque puede cambiarse para tu instalación de Python llamando a la función sys.setdefaultencoding(encoding) en una versión personalizada de site.py.

La combinación de cadenas de 8 bits y Unicode siempre fuerza conversión a Unicode, utilizando la codificación ASCII por defecto; el resultado de 'a' + u'bc' es u'abc'.

Se han añadido nuevas funciones incorporadas y se han modificado las existentes para que sean compatibles con Unicode:

  • unichr(ch) retorna una cadena Unicode de 1 carácter, que contiene el carácter ch.

  • ord(u), donde u es una cadena regular o Unicode de 1 carácter, retorna el número del carácter como un entero.

  • unicode(string [, encoding] [, errors] ) crea una cadena Unicode a partir de una cadena de 8 bits. encoding es una cadena que nombra la codificación a utilizar. El parámetro errors especifica el tratamiento de los caracteres que no son válidos para la codificación actual; pasar 'strict como valor hace que se lance una excepción en cualquier error de codificación, mientras que 'ignore hace que los errores se ignoren silenciosamente y 'replace utiliza U+FFFD, el carácter oficial de reemplazo, en caso de cualquier problema.

  • La sentencia exec, y varias funciones integradas como eval(), getattr(), y setattr() también aceptarán cadenas Unicode así como cadenas regulares. (Es posible que en el proceso de corrección de esto se hayan pasado por alto algunas funciones incorporadas; si encuentra una función incorporada que acepte cadenas pero que no acepte cadenas Unicode en absoluto, por favor, infórmelo como un error)

Un nuevo módulo, unicodedata, proporciona una interfaz para las propiedades de los caracteres Unicode. Por ejemplo, unicodedata.category(u'A') retorna la cadena de 2 caracteres “Lu”, la “L” denota que es una letra, y la “u” significa que es mayúscula. unicodedata.bidirectional(u'\u0660') retorna “AN”, lo que significa que U+0660 es un número árabe.

El módulo codecs contiene funciones para buscar codificaciones existentes y registrar otras nuevas. A menos que quiera implementar una nueva codificación, lo más habitual es que utilice la función codecs.lookup(encoding), que retorna una tupla de 4 elementos: (encode_func, decode_func, stream_reader, stream_writer).

  • encode_func es una función que toma una cadena Unicode, y retorna una 2-tupla (string, length). string es una cadena de 8 bits que contiene una porción (tal vez toda) de la cadena Unicode convertida a la codificación dada, y longitud indica la cantidad de cadena Unicode convertida.

  • decode_func es lo opuesto a encode_func, tomando una cadena de 8 bits y retornando una 2-tupla (ustring, length), que consiste en la cadena Unicode resultante ustring y el entero length que dice cuánto de la cadena de 8 bits se consumió.

  • stream_reader es una clase que soporta la decodificación de la entrada de un flujo. stream_reader(file_obj) retorna un objeto que soporta los métodos read(), readline() y readlines(). Todos estos métodos traducirán desde la codificación dada y retornarán cadenas Unicode.

  • stream_writer, de forma similar, es una clase que soporta la codificación de la salida a un flujo. stream_writer(file_obj) retorna un objeto que soporta los métodos write() y writelines(). Estos métodos esperan cadenas Unicode, traduciéndolas a la codificación dada en la salida.

Por ejemplo, el siguiente código escribe una cadena Unicode en un archivo, codificándola como UTF-8:

import codecs

unistr = u'\u0660\u2000ab ...'

(UTF8_encode, UTF8_decode,
 UTF8_streamreader, UTF8_streamwriter) = codecs.lookup('UTF-8')

output = UTF8_streamwriter( open( '/tmp/output', 'wb') )
output.write( unistr )
output.close()

El siguiente código leería la entrada UTF-8 del archivo:

input = UTF8_streamreader( open( '/tmp/output', 'rb') )
print repr(input.read())
input.close()

Las expresiones regulares compatibles con Unicode están disponibles a través del módulo re, que tiene una nueva implementación subyacente llamada SRE escrita por Fredrik Lundh de Secret Labs AB.

Se ha añadido una opción de línea de comandos -U que hace que el compilador de Python interprete todos los literales de cadena como literales de cadena Unicode. Esta opción está pensada para ser utilizada en las pruebas y para asegurar el futuro de su código Python, ya que alguna versión futura de Python puede dejar de soportar cadenas de 8 bits y proporcionar sólo cadenas Unicode.

Comprensión de listas

Las listas son un tipo de datos muy útil en Python, y muchos programas manipulan una lista en algún momento. Dos operaciones comunes en las listas son hacer un bucle sobre ellas, y escoger los elementos que cumplen un cierto criterio, o aplicar alguna función a cada elemento. Por ejemplo, dada una lista de cadenas, podrías querer sacar todas las cadenas que contengan una determinada subcadena, o quitar los espacios en blanco de cada línea.

Las funciones map() y filter() existentes pueden usarse para este propósito, pero requieren una función como uno de sus argumentos. Esto está bien si hay una función incorporada que se puede pasar directamente, pero si no la hay, hay que crear una pequeña función para hacer el trabajo requerido, y las reglas de ámbito de Python hacen que el resultado sea feo si la pequeña función necesita información adicional. Tomemos el primer ejemplo del párrafo anterior, encontrar todas las cadenas de la lista que contienen una subcadena dada. Podrías escribir lo siguiente para hacerlo:

# Given the list L, make a list of all strings
# containing the substring S.
sublist = filter( lambda s, substring=S:
                     string.find(s, substring) != -1,
                  L)

Debido a las reglas de ámbito de Python, se utiliza un argumento por defecto para que la función anónima creada por la expresión lambda sepa qué subcadena se está buscando. Las comprensiones de lista hacen esto más limpio:

sublist = [ s for s in L if string.find(s, S) != -1 ]

Las comprensiones de listas tienen la forma:

[ expression for expr in sequence1
             for expr2 in sequence2 ...
             for exprN in sequenceN
             if condition ]

Las cláusulas forin contienen las secuencias a iterar. Las secuencias no tienen por qué tener la misma longitud, ya que no se itera sobre ellas en paralelo, sino de izquierda a derecha; esto se explica más claramente en los párrafos siguientes. Los elementos de la lista generada serán los valores sucesivos de la expresión. La cláusula final if es opcional; si está presente, la expresión sólo se evalúa y se añade al resultado si la condición es verdadera.

Para dejar muy clara la semántica, una comprensión de lista equivale al siguiente código de Python:

for expr1 in sequence1:
    for expr2 in sequence2:
    ...
        for exprN in sequenceN:
             if (condition):
                  # Append the value of
                  # the expression to the
                  # resulting list.

Esto significa que cuando hay múltiples cláusulas forin, la lista resultante será igual al producto de las longitudes de todas las secuencias. Si tiene dos listas de longitud 3, la lista de salida tendrá 9 elementos:

seq1 = 'abc'
seq2 = (1,2,3)
>>> [ (x,y) for x in seq1 for y in seq2]
[('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3), ('c', 1),
('c', 2), ('c', 3)]

Para evitar introducir una ambigüedad en la gramática de Python, si expresión está creando una tupla, debe estar rodeada de paréntesis. La primera comprensión de la lista a continuación es un error de sintaxis, mientras que la segunda es correcta:

# Syntax error
[ x,y for x in seq1 for y in seq2]
# Correct
[ (x,y) for x in seq1 for y in seq2]

La idea de las comprensiones de listas procede originalmente del lenguaje de programación funcional Haskell (https://www.haskell.org). Greg Ewing fue el que más abogó por añadirlas a Python y escribió el parche inicial de comprensión de listas, que luego se discutió durante un tiempo aparentemente interminable en la lista de correo de python-dev y se mantuvo actualizada por Skip Montanaro.

Asignación aumentada

Los operadores de asignación aumentados, otra característica largamente solicitada, han sido añadidos a Python 2.0. Los operadores de asignación aumentados incluyen +=, -=, *=, etc. Por ejemplo, la sentencia a += 2 incrementa el valor de la variable a en 2, lo que equivale a la sentencia algo más larga a = a + 2.

La lista completa de operadores de asignación soportados es +=, -=, *=, /=, %=, **=, &=, |=, ^=, >>=, y <=. Las clases de Python pueden anular los operadores de asignación aumentados definiendo métodos llamados __iadd__(), __isub__(), etc. Por ejemplo, la siguiente clase Number almacena un número y soporta el uso de += para crear una nueva instancia con un valor incrementado.

class Number:
    def __init__(self, value):
        self.value = value
    def __iadd__(self, increment):
        return Number( self.value + increment)

n = Number(5)
n += 3
print n.value

El método especial __iadd__() es llamado con el valor del incremento, y debe retornar una nueva instancia con un valor adecuadamente modificado; este valor de retorno se vincula como el nuevo valor de la variable del lado izquierdo.

Los operadores de asignación aumentada se introdujeron por primera vez en el lenguaje de programación C, y la mayoría de los lenguajes derivados de C, como awk, C++, Java, Perl y PHP también los soportan. El parche de asignación aumentada fue implementado por Thomas Wouters.

Métodos de cadena de caracteres

Hasta ahora, la funcionalidad de manipulación de cadenas de caracteres estaba en el módulo string, que normalmente era un front-end para el módulo strop escrito en C. La adición de Unicode supuso una dificultad para el módulo strop, porque todas las funciones tendrían que ser reescritas para aceptar cadenas de 8 bits o Unicode. Para funciones como string.replace(), que toma 3 argumentos de cadena, eso significa ocho posibles permutaciones, y el correspondiente código complicado.

En cambio, Python 2.0 traslada el problema al tipo de cadena de caracteres, haciendo que la funcionalidad de manipulación de cadenas esté disponible a través de métodos tanto en cadenas de 8 bits como en cadenas Unicode.

>>> 'andrew'.capitalize()
'Andrew'
>>> 'hostname'.replace('os', 'linux')
'hlinuxtname'
>>> 'moshe'.find('sh')
2

Una cosa que no ha cambiado, a pesar de una notable broma de April Fools, es que las cadenas de Python son inmutables. Así, los métodos de cadenas retornan cadenas nuevas, y no modifican la cadena sobre la que operan.

El antiguo módulo string sigue existiendo por compatibilidad con el pasado, pero actúa principalmente como un front-end para los nuevos métodos de cadena de caracteres.

Dos métodos que no tienen paralelo en las versiones anteriores a la 2.0, aunque existieron en JPython durante bastante tiempo, son startswith() y endswith(). s.startswith(t) es equivalente a s[:len(t)] == t, mientras que s.endswith(t) es equivalente a s[-len(t):] == t.

Otro método que merece una mención especial es join(). El método join() de una cadena recibe un parámetro, una secuencia de cadenas, y es equivalente a la función string.join() del antiguo módulo string, con los argumentos invertidos. En otras palabras, s.join(seq) es equivalente a la antigua string.join(seq, s).

Recogida de basura de los ciclos

La implementación en C de Python utiliza el conteo de referencias para implementar la recolección de basura. Cada objeto de Python mantiene un recuento del número de referencias que apuntan a sí mismo, y ajusta el recuento a medida que se crean o destruyen referencias. Una vez que el recuento de referencias llega a cero, el objeto deja de ser accesible, ya que es necesario tener una referencia a un objeto para acceder a él, y si el recuento es cero, ya no existen referencias.

El conteo de referencias tiene algunas propiedades agradables: es fácil de entender e implementar, y la implementación resultante es portable, bastante rápida, y reacciona bien con otras bibliotecas que implementan sus propios esquemas de manejo de memoria. El mayor problema del conteo de referencias es que a veces no se da cuenta de que los objetos ya no son accesibles, lo que provoca una fuga de memoria. Esto ocurre cuando hay ciclos de referencias.

Consideremos el ciclo más simple posible, una instancia de clase que tiene una referencia a sí misma:

instance = SomeClass()
instance.myself = instance

Después de ejecutar las dos líneas de código anteriores, la cuenta de referencias de instance es 2; una referencia es de la variable llamada 'instance, y la otra es del atributo myself de la instancia.

Si la siguiente línea de código es del instance, ¿qué ocurre? La cuenta de referencias de instance se reduce en 1, por lo que tiene una cuenta de referencias de 1; la referencia en el atributo myself sigue existiendo. Sin embargo, la instancia ya no es accesible a través del código de Python, y podría ser eliminada. Varios objetos pueden participar en un ciclo si tienen referencias entre sí, haciendo que todos los objetos se filtren.

Python 2.0 soluciona este problema ejecutando periódicamente un algoritmo de detección de ciclos que busca los ciclos inaccesibles y borra los objetos implicados. Un nuevo módulo gc proporciona funciones para realizar una recolección de basura, obtener estadísticas de depuración y afinar los parámetros del recolector.

Ejecutar el algoritmo de detección de ciclos lleva algo de tiempo, y por lo tanto resultará en una sobrecarga adicional. Se espera que después de que hayamos adquirido experiencia con la recogida de ciclos al utilizar la versión 2.0, Python 2.1 sea capaz de minimizar la sobrecarga con un cuidadoso ajuste. Todavía no es obvio cuánto rendimiento se pierde, porque la evaluación comparativa de esto es difícil y depende crucialmente de la frecuencia con la que el programa crea y destruye objetos. La detección de ciclos puede ser desactivada cuando Python es compilado, si no puede permitirse ni siquiera una pequeña penalización de velocidad o sospecha que la recolección de ciclos es un error, especificando la opción --without-cycle-gc cuando se ejecuta el script configure.

Varias personas abordaron este problema y contribuyeron a una solución. Una primera implementación del enfoque de detección de ciclos fue escrita por Toby Kelsey. El algoritmo actual fue sugerido por Eric Tiedemann durante una visita al CNRI, y Guido van Rossum y Neil Schemenauer escribieron dos implementaciones diferentes, que posteriormente fueron integradas por Neil. Muchas otras personas ofrecieron sugerencias a lo largo del camino; los archivos de marzo de 2000 de la lista de correo python-dev contienen la mayor parte de la discusión relevante, especialmente en los hilos titulados «Colección de ciclos de referencia para Python» y «Finalización de nuevo».

Otros cambios en el núcleo

Se han realizado varios cambios menores en la sintaxis y las funciones incorporadas de Python. Ninguno de los cambios es de gran alcance, pero son conveniencias prácticas.

Cambios menores del lenguaje

Una nueva sintaxis hace más conveniente llamar a una función dada con una tupla de argumentos y/o un diccionario de argumentos de palabras clave. En Python 1.5 y anteriores, se utilizaba la función incorporada apply(): apply(f, args, kw) llama a la función f() con la tupla de argumentos args y los argumentos de palabras clave en el diccionario kw. apply() es lo mismo en 2.0, pero gracias a un parche de Greg Ewing, f(*args, **kw) es una forma más corta y clara de conseguir el mismo efecto. Esta sintaxis es simétrica con la sintaxis para definir funciones:

def f(*args, **kw):
    # args is a tuple of positional args,
    # kw is a dictionary of keyword args
    ...

La sentencia print puede ahora tener su salida dirigida a un objeto tipo archivo siguiendo a print con >> archivo, similar al operador de redirección en los shells de Unix. Antes había que utilizar el método write() del objeto tipo archivo, que carece de la comodidad y simplicidad de print, o bien asignar un nuevo valor a sys.stdout y luego restaurar el valor anterior. Para enviar la salida al error estándar, es mucho más fácil escribir esto:

print >> sys.stderr, "Warning: action field not supplied"

Ahora se puede cambiar el nombre de los módulos al importarlos, utilizando la sintaxis import module as name o from module import name as othername. El parche fue enviado por Thomas Wouters.

Un nuevo estilo de formato está disponible cuando se utiliza el operador %; “%r” insertará el repr() de su argumento. Esto también se añadió por consideraciones de simetría, esta vez por simetría con el estilo de formato existente “%s”, que inserta el str() de su argumento. Por ejemplo, '%r %s' % ('abc', 'abc') retorna una cadena que contiene 'abc' abc.

Anteriormente no había forma de implementar una clase que sobrepasara el operador incorporado de Python in e implementara una versión personalizada.``obj in seq`` retorna verdadero si obj está presente en la secuencia seq; Python lo calcula simplemente probando cada índice de la secuencia hasta que se encuentra obj o se encuentra un IndexError. Moshe Zadka ha contribuido con un parche que añade un método mágico __contains__() para proporcionar una implementación personalizada para in. Además, los nuevos objetos incorporados escritos en C pueden definir lo que in significa para ellos a través de una nueva ranura en el protocolo de secuencia.

Las versiones anteriores de Python utilizaban un algoritmo recursivo para borrar objetos. Las estructuras de datos muy anidadas podían hacer que el intérprete llenara la pila de C y se bloqueara; Christian Tismer reescribió la lógica de borrado para solucionar este problema. En una nota relacionada, la comparación de objetos recursivos se repite infinitamente y se bloquea; Jeremy Hylton reescribió el código para que no se bloquee, produciendo un resultado útil. Por ejemplo, después de este código:

a = []
b = []
a.append(a)
b.append(b)

La comparación a==b retorna verdadero, porque las dos estructuras de datos recursivas son isomorfas. Véase el hilo «trashcan and PR#7» en los archivos de abril de 2000 de la lista de correo de python-dev para la discusión que condujo a esta implementación, y algunos enlaces relevantes útiles. Tenga en cuenta que las comparaciones ahora también pueden generar excepciones. En versiones anteriores de Python, una operación de comparación como cmp(a,b) siempre producía una respuesta, incluso si un método __cmp__() definido por el usuario encontraba un error, ya que la excepción resultante simplemente se tragaba en silencio.

Se ha trabajado en portar Python a Windows de 64 bits en el procesador Itanium, principalmente por Trent Mick de ActiveState. (Confusamente, sys.platform sigue siendo 'win32 en Win64 porque parece que para facilitar la portabilidad, MS Visual C++ trata el código como de 32 bits en Itanium) PythonWin también es compatible con Windows CE; vea la página de Python CE en http://pythonce.sourceforge.net/ para más información.

Otra plataforma nueva es Darwin/MacOS X; el soporte inicial para ella está en Python 2.0. La carga dinámica funciona, si se especifica «configure –with-dyld –with-suffix=.x». Consulte el README de la distribución de fuentes de Python para obtener más instrucciones.

Se ha intentado aliviar uno de los defectos de Python, la a menudo confusa excepción NameError cuando el código hace referencia a una variable local antes de que se le haya asignado un valor. Por ejemplo, el siguiente código lanza una excepción en la sentencia print tanto en 1.5.2 como en 2.0; en 1.5.2 se lanza una excepción NameError, mientras que en 2.0 se lanza una nueva excepción UnboundLocalError. UnboundLocalError es una subclase de NameError, así que cualquier código existente que espere que se lance NameError debería seguir funcionando.

def f():
    print "i=",i
    i = i + 1
f()

Se han introducido dos nuevas excepciones, TabError y IndentationError. Ambas son subclases de SyntaxError, y se lanzan cuando el código Python se encuentra con una sangría incorrecta.

Cambios en las funciones incorporadas

Se ha añadido un nuevo built-in, zip(seq1, seq2, ...). zip() retorna una lista de tuplas donde cada tupla contiene el i-ésimo elemento de cada una de las secuencias del argumento. La diferencia entre zip() y map(None, seq1, seq2) es que map() rellena las secuencias con None si las secuencias no tienen la misma longitud, mientras que zip() trunca la lista retornada a la longitud de la secuencia argumental más corta.

Las funciones int() y long() aceptan ahora un parámetro «base» opcional cuando el primer argumento es una cadena. int(“123”, 10)` retorna 123, mientras que int('123', 16) retorna 291. int(123, 16) lanza una excepción TypeError con el mensaje «no se puede convertir una cadena con base explícita».

Se ha añadido al módulo sys una nueva variable que contiene información más detallada sobre la versión. sys.version_info es una tupla (major, minor, micro, level, serial) Por ejemplo, en una hipotética 2.0.1beta1, sys.version_info sería (2, 0, 1, 'beta', 1). level es una cadena como "alpha", "beta", o "final" para una versión final.

Los diccionarios tienen un nuevo y extraño método, setdefault(key, default), que se comporta de forma similar al método get() existente. Sin embargo, si falta la clave, setdefault() retorna el valor de default como haría get(), y también lo inserta en el diccionario como valor de key. Así, las siguientes líneas de código:

if dict.has_key( key ): return dict[key]
else:
    dict[key] = []
    return dict[key]

puede reducirse a una única sentencia return dict.setdefault(key, []).

El intérprete establece una profundidad de recursión máxima para atrapar la recursión desbocada antes de llenar la pila de C y causar un volcado del núcleo o GPF. Anteriormente este límite se fijaba cuando se compilaba Python, pero en la versión 2.0 la profundidad máxima de recursión puede leerse y modificarse usando sys.getrecursionlimit() y sys.setrecursionlimit(). El valor por defecto es 1000, y se puede encontrar un valor máximo aproximado para una plataforma determinada ejecutando un nuevo script, Misc/find_recursionlimit.py.

Adaptación a la versión 2.0

Las nuevas versiones de Python se esfuerzan por ser compatibles con las anteriores, y el historial ha sido bastante bueno. Sin embargo, algunos cambios se consideran lo suficientemente útiles, normalmente porque corrigen decisiones de diseño iniciales que resultaron ser activamente erróneas, que no siempre se puede evitar romper la compatibilidad hacia atrás. Esta sección enumera los cambios en Python 2.0 que pueden hacer que el código Python antiguo se rompa.

El cambio que probablemente romperá la mayor parte del código es el endurecimiento de los argumentos aceptados por algunos métodos. Algunos métodos tomaban múltiples argumentos y los trataban como una tupla, particularmente varios métodos de lista como append() y insert(). En versiones anteriores de Python, si L es una lista, L.append( 1,2 ) añade la tupla (1,2) a la lista. En Python 2.0 esto provoca una excepción TypeError con el mensaje “append requiere exactamente 1 argumento; se han dado 2”. La solución es simplemente añadir un conjunto extra de paréntesis para pasar ambos valores como una tupla: L.append( (1,2) ).

Las versiones anteriores de estos métodos eran más indulgentes porque utilizaban una antigua función de la interfaz C de Python para analizar sus argumentos; la versión 2.0 los moderniza para utilizar PyArg_ParseTuple(), la función actual de análisis de argumentos, que proporciona mensajes de error más útiles y trata las llamadas con múltiples argumentos como errores. Si es absolutamente necesario usar la versión 2.0 pero no puedes arreglar tu código, puedes editar Objects/listobject.c y definir el símbolo del preprocesador NO_STRICT_LIST_APPEND para preservar el antiguo comportamiento; esto no es recomendable.

Algunas de las funciones del módulo socket siguen siendo indulgentes en este sentido. Por ejemplo, socket.connect( ('hostname', 25) )() es la forma correcta, pasando una tupla que representa una dirección IP, pero socket.connect( 'hostname', 25 )() también funciona. socket.connect_ex() y socket.bind() son igualmente fáciles de usar. 2.0alpha1 endureció estas funciones, pero como la documentación utilizaba la forma errónea de argumentos múltiples, mucha gente escribió código que se rompería con la comprobación más estricta. GvR se echó atrás en los cambios ante la reacción del público, así que para el módulo socket, la documentación se arregló y la forma de argumento múltiple simplemente se marcó como obsoleta; se reforzará de nuevo en una futura versión de Python.

El escape \x en los literales de cadena ahora toma exactamente 2 dígitos hexadecimales. Antes consumía todos los dígitos hexadecimales que seguían a la “x” y tomaba los 8 bits más bajos del resultado, por lo que \x123456 era equivalente a \x56.

Las excepciones AttributeError y NameError tienen un mensaje de error más amigable, cuyo texto será algo así como 'Spam' instance has no attribute 'eggs' o name 'eggs' is not defined. Anteriormente, el mensaje de error era simplemente la falta del nombre del atributo eggs, y el código escrito para aprovechar este hecho se romperá en la versión 2.0.

Se ha trabajado para que los enteros y los enteros largos sean un poco más intercambiables. En la versión 1.5.2, se añadió soporte para archivos grandes en Solaris, para permitir la lectura de archivos de más de 2 GiB; esto hizo que el método tell() de los objetos de archivo retornara un entero largo en lugar de un entero normal. Algunos códigos restaban dos desplazamientos de archivos e intentaban utilizar el resultado para multiplicar una secuencia o cortar una cadena, pero esto generaba un TypeError. En la versión 2.0, los enteros largos pueden utilizarse para multiplicar o cortar una secuencia, y se comportarán como se espera intuitivamente; 3L * 'abc' produce “abcabcabc”, y (0,1,2,3)[2L:4L] produce (2,3). Los enteros largos también pueden utilizarse en varios contextos en los que antes sólo se aceptaban enteros, como en el método seek() de los objetos de archivo, y en los formatos soportados por el operador % (%d, %i, %x, etc.). Por ejemplo, "%d" % 2L**64 producirá la cadena 18446744073709551616.

El cambio más sutil de los enteros largos es que el str() de un entero largo ya no tiene un carácter “L” al final, aunque repr() todavía lo incluye. La “L” molestaba a muchas personas que querían imprimir enteros largos con el mismo aspecto que los enteros normales, ya que tenían que esforzarse por cortar el carácter. Esto ya no es un problema en 2.0, pero el código que hace str(longval)[:-1] y asume que la “L” está ahí, ahora perderá el dígito final.

Tomar el repr() de un flotador utiliza ahora una precisión de formato diferente a la de str(). repr() utiliza la cadena de formato %.17g para el sprintf() de C, mientras que str() utiliza %.12g como antes. El efecto es que repr() puede mostrar ocasionalmente más decimales que str(), para ciertos números. Por ejemplo, el número 8,1 no puede representarse exactamente en binario, por lo que repr(8,1) es '8,09999999999996', mientras que str(8,1) es '8,1'.

La opción de línea de comandos -X, que convertía todas las excepciones estándar en cadenas en lugar de clases, ha sido eliminada; las excepciones estándar serán ahora siempre clases. El módulo exceptions que contiene las excepciones estándar ha sido traducido de Python a un módulo C integrado, escrito por Barry Warsaw y Fredrik Lundh.

Extensión/Incorporación de cambios

Algunos de los cambios están bajo la cubierta, y sólo serán evidentes para la gente que escribe módulos de extensión de C o que incrusta un intérprete de Python en una aplicación más grande. Si no estás tratando con la API de C de Python, puedes saltarte esta sección.

El número de versión de la API C de Python se incrementó, por lo que las extensiones C compiladas para 1.5.2 deben ser recompiladas para que funcionen con 2.0. En Windows, no es posible que Python 2.0 importe una extensión de terceros construida para Python 1.5.x debido a cómo funcionan las DLL de Windows, por lo que Python lanzará una excepción y la importación fallará.

Los usuarios del módulo ExtensionClass de Jim Fulton estarán encantados de saber que se han añadido ganchos para que las ExtensionClasses sean ahora compatibles con isinstance() y issubclass(). Esto significa que ya no tiene que recordar escribir código como if type(obj) == myExtensionClass, sino que puede utilizar el más natural if isinstance(obj, myExtensionClass).

El archivo Python/importdl.c, que era una masa de #ifdefs para soportar la carga dinámica en muchas plataformas diferentes, fue limpiado y reorganizado por Greg Stein. importdl.c es ahora bastante pequeño, y el código específico de la plataforma se ha movido a un montón de archivos Python/dynload_*.c. Otra limpieza: también había una serie de archivos my*.h en el directorio Include/ que contenían varios hacks de portabilidad; se han fusionado en un único archivo, Include/pyport.h.

Se ha completado la tan esperada reestructuración de malloc de Vladimir Marangozov, para facilitar que el intérprete de Python utilice un asignador personalizado en lugar del estándar de C malloc(). Para la documentación, lea los comentarios en Include/pymem.h y Include/objimpl.h. Para ver las largas discusiones durante las cuales se elaboró la interfaz, consulte los archivos web de las listas “patches” y “python-dev” en python.org.

Las versiones recientes del entorno de desarrollo GUSI para MacOS soportan hilos POSIX. Por lo tanto, el soporte de hilos POSIX de Python ahora funciona en Macintosh. También se ha contribuido al soporte de hilos utilizando la biblioteca GNU pth del espacio de usuario.

También se ha mejorado el soporte de hilos en Windows. Windows soporta bloqueos de hilos que utilizan objetos del núcleo sólo en caso de contención; en el caso común cuando no hay contención, utilizan funciones más simples que son un orden de magnitud más rápido. Una versión con hilos de Python 1.5.2 en NT es dos veces más lenta que una versión sin hilos; con los cambios de la 2.0, la diferencia es sólo del 10%. Estas mejoras fueron aportadas por Yakov Markovitch.

El código fuente de Python 2.0 ahora sólo utiliza prototipos ANSI C, por lo que la compilación de Python ahora requiere un compilador ANSI C, y ya no puede hacerse utilizando un compilador que sólo soporte K&R C.

Anteriormente, la máquina virtual de Python utilizaba números de 16 bits en su bytecode, lo que limitaba el tamaño de los archivos fuente. En particular, esto afectaba al tamaño máximo de las listas literales y los diccionarios en el código fuente de Python; ocasionalmente, las personas que generan código Python se encontraban con este límite. Un parche de Charles G. Waldman eleva el límite de 2^16 a 2^{32}.

Se han añadido tres nuevas funciones para añadir constantes al diccionario de un módulo en el momento de la inicialización: PyModule_AddObject(), PyModule_AddIntConstant(), y PyModule_AddStringConstant(). Cada una de estas funciones toma un objeto de módulo, una cadena C terminada en cero que contiene el nombre a añadir, y un tercer argumento para el valor a asignar al nombre. Este tercer argumento es, respectivamente, un objeto Python, un C long o una cadena C.

Se ha añadido una API envolvente para los manejadores de señales de estilo Unix. PyOS_getsig() obtiene un manejador de señales y PyOS_setsig() establecerá un nuevo manejador.

Distutils: Facilitando la instalación de módulos

Antes de Python 2.0, la instalación de módulos era un asunto tedioso – no había forma de averiguar automáticamente dónde se instalaba Python, o qué opciones del compilador se debían usar para los módulos de extensión. Los autores de software tenían que pasar por un arduo ritual de edición de Makefiles y archivos de configuración, que sólo funcionaban realmente en Unix y dejaban sin soporte a Windows y MacOS. Los usuarios de Python se enfrentaban a instrucciones de instalación muy diferentes que variaban entre los distintos paquetes de extensión, lo que hacía que la administración de una instalación de Python fuera una tarea ardua.

El SIG de utilidades de distribución, liderado por Greg Ward, ha creado las Distutils, un sistema para facilitar la instalación de paquetes. Forman el paquete distutils, una nueva parte de la biblioteca estándar de Python. En el mejor de los casos, la instalación de un módulo de Python desde el código fuente requerirá los mismos pasos: primero simplemente hay que desempaquetar el archivo tar o zip, y ejecutar «python setup.py install. La plataforma será detectada automáticamente, el compilador será reconocido, los módulos de extensión C serán compilados, y la distribución será instalada en el directorio apropiado. Los argumentos opcionales de la línea de comandos proporcionan más control sobre el proceso de instalación, el paquete distutils ofrece muchos lugares para anular los valores predeterminados - separando la construcción de la instalación, construyendo o instalando en directorios no predeterminados, y más.

Para usar las Distutils, necesitas escribir un script setup.py. Para el caso simple, cuando el software contiene sólo archivos .py, un setup.py mínimo puede tener sólo unas pocas líneas:

from distutils.core import setup
setup (name = "foo", version = "1.0",
       py_modules = ["module1", "module2"])

El archivo setup.py no es mucho más complicado si el software consta de unos pocos paquetes:

from distutils.core import setup
setup (name = "foo", version = "1.0",
       packages = ["package", "package.subpackage"])

Una extensión en C puede ser el caso más complicado; he aquí un ejemplo tomado del paquete PyXML:

from distutils.core import setup, Extension

expat_extension = Extension('xml.parsers.pyexpat',
     define_macros = [('XML_NS', None)],
     include_dirs = [ 'extensions/expat/xmltok',
                      'extensions/expat/xmlparse' ],
     sources = [ 'extensions/pyexpat.c',
                 'extensions/expat/xmltok/xmltok.c',
                 'extensions/expat/xmltok/xmlrole.c', ]
       )
setup (name = "PyXML", version = "0.5.4",
       ext_modules =[ expat_extension ] )

Las Distutils también pueden encargarse de crear distribuciones fuente y binarias. El comando «sdist», ejecutado por «python setup.py sdist, construye una distribución fuente como foo-1.0.tar.gz. Añadir nuevos comandos no es difícil, ya se han aportado los comandos «bdist_rpm» y «bdist_wininst» para crear una distribución RPM y un instalador de Windows para el software, respectivamente. Los comandos para crear otros formatos de distribución, como los paquetes de Debian y los archivos .pkg de Solaris, se encuentran en diversas etapas de desarrollo.

Todo esto está documentado en un nuevo manual, Distribución de módulos de Python, que se une al conjunto básico de documentación de Python.

Módulos XML

La versión 1.5.2 de Python incluía un sencillo analizador XML en forma de módulo xmllib, aportado por Sjoerd Mullender. Desde el lanzamiento de la versión 1.5.2, se han generalizado dos interfaces diferentes para el procesamiento de XML: SAX2 (versión 2 de la API Simple para XML) proporciona una interfaz basada en eventos con algunas similitudes con xmllib, y el DOM (Modelo de Objetos de Documento) proporciona una interfaz basada en un árbol, transformando un documento XML en un árbol de nodos que puede ser atravesado y modificado. Python 2.0 incluye una interfaz SAX2 y una interfaz DOM reducida como parte del paquete xml. Aquí daremos una breve descripción de estas nuevas interfaces; consulte la documentación de Python o el código fuente para obtener detalles completos. El SIG XML de Python también está trabajando en la mejora de la documentación.

Soporte de SAX2

SAX define una interfaz basada en eventos para analizar XML. Para usar SAX, debes escribir una clase manejadora de SAX. Las clases manejadoras heredan de varias clases proporcionadas por SAX, y sobrescriben varios métodos que luego serán llamados por el analizador XML. Por ejemplo, los métodos startElement() y endElement() son llamados para cada etiqueta inicial y final encontrada por el analizador, el método characters() es llamado para cada trozo de datos de caracteres, etc.

La ventaja del enfoque basado en eventos es que todo el documento no tiene que residir en la memoria en un momento dado, lo cual es importante si estás procesando documentos realmente enormes. Sin embargo, escribir la clase manejadora de SAX puede ser muy complicado si se intenta modificar la estructura del documento de alguna manera elaborada.

Por ejemplo, este pequeño programa de ejemplo define un manejador que imprime un mensaje para cada etiqueta inicial y final, y luego analiza el archivo hamlet.xml usándolo:

from xml import sax

class SimpleHandler(sax.ContentHandler):
    def startElement(self, name, attrs):
        print 'Start of element:', name, attrs.keys()

    def endElement(self, name):
        print 'End of element:', name

# Create a parser object
parser = sax.make_parser()

# Tell it what handler to use
handler = SimpleHandler()
parser.setContentHandler( handler )

# Parse a file!
parser.parse( 'hamlet.xml' )

Para más información, consulte la documentación de Python o el XML HOWTO en http://pyxml.sourceforge.net/topics/howto/xml-howto.html.

Soporte DOM

El Modelo de Objetos del Documento es una representación basada en un árbol para un documento XML. Una instancia de Document de nivel superior es la raíz del árbol, y tiene un único hijo que es la instancia de Element de nivel superior. Este Element tiene nodos hijos que representan los datos de los caracteres y cualquier subelemento, que puede tener otros hijos propios, y así sucesivamente. Utilizando el DOM puedes recorrer el árbol resultante como quieras, acceder a los valores de los elementos y atributos, insertar y eliminar nodos y volver a convertir el árbol en XML.

El DOM es útil para modificar documentos XML, porque se puede crear un árbol DOM, modificarlo añadiendo nuevos nodos o reordenando subárboles, y luego producir un nuevo documento XML como salida. También se puede construir un árbol DOM manualmente y convertirlo en XML, lo que puede ser una forma más flexible de producir una salida XML que simplemente escribir <tag1></tag1> un archivo.

La implementación del DOM incluida en Python se encuentra en el módulo xml.dom.minidom. Es una implementación ligera del DOM de nivel 1 con soporte para espacios de nombres XML. Las funciones parse() y parseString() se proporcionan para generar un árbol DOM:

from xml.dom import minidom
doc = minidom.parse('hamlet.xml')

doc es una instancia de Document. El Document, al igual que el resto de clases del DOM como el Element y el Text, es una subclase de la clase base Node. Por lo tanto, todos los nodos de un árbol DOM soportan ciertos métodos comunes, como toxml() que retorna una cadena que contiene la representación XML del nodo y sus hijos. Cada clase también tiene métodos especiales propios; por ejemplo, las instancias Element y Document tienen un método para encontrar todos los elementos hijos con un nombre de etiqueta dado. Continuando con el ejemplo anterior de 2 líneas:

perslist = doc.getElementsByTagName( 'PERSONA' )
print perslist[0].toxml()
print perslist[1].toxml()

Para el archivo XML Hamlet, las líneas anteriores dan como resultado:

<PERSONA>CLAUDIUS, king of Denmark. </PERSONA>
<PERSONA>HAMLET, son to the late, and nephew to the present king.</PERSONA>

El elemento raíz del documento está disponible como doc.documentElement, y sus hijos pueden modificarse fácilmente borrando, añadiendo o eliminando nodos:

root = doc.documentElement

# Remove the first child
root.removeChild( root.childNodes[0] )

# Move the new first child to the end
root.appendChild( root.childNodes[0] )

# Insert the new first child (originally,
# the third child) before the 20th child.
root.insertBefore( root.childNodes[0], root.childNodes[20] )

Una vez más, te remito a la documentación de Python para obtener una lista completa de las diferentes clases Node y sus diversos métodos.

Relación con PyXML

El Grupo de Interés Especial XML lleva un tiempo trabajando en código Python relacionado con XML. Su distribución de código, llamada PyXML, está disponible en las páginas web del SIG en https://www.python.org/community/sigs/current/xml-sig. La distribución de PyXML también utiliza el nombre de paquete xml. Si has escrito programas que utilizan PyXML, probablemente te preguntes sobre su compatibilidad con el paquete 2.0 xml.

La respuesta es que el paquete xml de Python 2.0 no es compatible con PyXML, pero puede hacerse compatible instalando una versión reciente de PyXML. Muchas aplicaciones pueden arreglárselas con el soporte XML que se incluye en Python 2.0, pero las aplicaciones más complicadas requerirán que se instale el paquete PyXML completo. Cuando se instala, las versiones 0.6.0 o superiores de PyXML sustituyen al paquete xml que se entrega con Python, y son un estricto superconjunto del paquete estándar, añadiendo un montón de características adicionales. Algunas de las características adicionales de PyXML incluyen:

  • 4DOM, una implementación completa de DOM de FourThought, Inc.

  • El parser de validación xmlproc, escrito por Lars Marius Garshol.

  • El módulo acelerador del parser sgmlop, escrito por Fredrik Lundh.

Cambios en los módulos

Se han realizado muchas mejoras y correcciones de errores en la extensa biblioteca estándar de Python; algunos de los módulos afectados son readline, ConfigParser, cgi, calendar, posix, readline, xmllib, aifc, chunk, wave, random, shelve, y nntplib. Consulte los registros de CVS para conocer los detalles exactos parche por parche.

Brian Gallew ha contribuido al soporte de OpenSSL para el módulo socket. OpenSSL es una implementación de Secure Socket Layer, que encripta los datos que se envían a través de un socket. Al compilar Python, puedes editar Modules/Setup para incluir el soporte de SSL, que añade una función adicional al módulo socket: socket.ssl(socket, keyfile, certfile), que toma un objeto socket y retorna un socket SSL. Los módulos httplib y urllib también han sido modificados para soportar URLs https://, aunque nadie ha implementado FTP o SMTP sobre SSL.

El módulo httplib ha sido reescrito por Greg Stein para soportar HTTP/1.1. Se proporciona compatibilidad con la versión 1.5 de httplib, aunque el uso de las características de HTTP/1.1, como el pipelining, requerirá reescribir el código para utilizar un conjunto diferente de interfaces.

El módulo Tkinter soporta ahora la versión 8.1, 8.2 o 8.3 de Tcl/Tk, y se ha eliminado el soporte para las versiones 7.x más antiguas. El módulo Tkinter ahora soporta la visualización de cadenas Unicode en los widgets Tk. Además, Fredrik Lundh ha contribuido con una optimización que hace que operaciones como create_line y create_polygon sean mucho más rápidas, especialmente cuando se utilizan muchas coordenadas.

El módulo curses ha sido ampliado en gran medida, a partir de la versión mejorada de Oliver Andrich, para proporcionar muchas funciones adicionales de los curses ncurses y SYSV, como el color, el soporte de conjuntos de caracteres alternativos, los pads y el soporte de ratón. Esto significa que el módulo ya no es compatible con los sistemas operativos que sólo tienen curses BSD, pero no parece haber ningún sistema operativo actualmente mantenido que caiga en esta categoría.

Como se mencionó en la discusión anterior sobre el soporte Unicode de la 2.0, la implementación subyacente de las expresiones regulares proporcionadas por el módulo re ha sido cambiada. SRE, un nuevo motor de expresiones regulares escrito por Fredrik Lundh y parcialmente financiado por Hewlett Packard, soporta la comparación con cadenas de 8 bits y cadenas Unicode.

Nuevos módulos

Se han añadido varios módulos nuevos. Nos limitaremos a enumerarlos con breves descripciones; consulte la documentación de la versión 2.0 para conocer los detalles de un módulo concreto.

  • atexit: Para registrar las funciones que serán llamadas antes de que el intérprete de Python salga. El código que actualmente establece sys.exitfunc directamente debe cambiarse para usar el módulo atexit en su lugar, importando atexit y llamando a atexit.register() con la función a llamar al salir. (Contribución de Skip Montanaro)

  • codecs, encodings, unicodedata: Añadidos como parte del nuevo soporte de Unicode.

  • filecmp: Sustituye a los antiguos módulos cmp, cmpcache y dircmp, que han quedado obsoletos. (Contribución de Gordon MacMillan y Moshe Zadka)

  • gettext: Este módulo proporciona soporte de internacionalización (I18N) y localización (L10N) para los programas de Python, proporcionando una interfaz a la biblioteca de catálogo de mensajes GNU gettext. (Integrado por Barry Warsaw, a partir de contribuciones separadas de Martin von Löwis, Peter Funk y James Henstridge)

  • linuxaudiodev: Soporte para el dispositivo /dev/audio en Linux, un gemelo del módulo existente sunaudiodev. (Contribuido por Peter Bosch, con correcciones de Jeremy Hylton)

  • mmap: Una interfaz para archivos mapeados en memoria tanto en Windows como en Unix. El contenido de un fichero puede ser mapeado directamente en memoria, en cuyo momento se comporta como una cadena mutable, por lo que su contenido puede ser leído y modificado. Incluso pueden pasarse a funciones que esperan cadenas ordinarias, como el módulo re. (Contribución de Sam Rushing, con algunas extensiones de A.M. Kuchling)

  • pyexpat: Una interfaz para el analizador XML de Expat. (Contribuido por Paul Prescod.)

  • robotparser: Analiza un archivo robots.txt, que se utiliza para escribir arañas web que evitan amablemente ciertas áreas de un sitio web. El analizador acepta el contenido de un archivo robots.txt, construye un conjunto de reglas a partir de él y puede responder a preguntas sobre la capacidad de búsqueda de una URL determinada. (Contribución de Skip Montanaro)

  • tabnanny: Un módulo/script para comprobar el código fuente de Python en busca de sangrías ambiguas. (Contribuido por Tim Peters.)

  • UserString: Una clase base útil para derivar objetos que se comportan como cadenas.

  • webbrowser: Un módulo que proporciona una forma independiente de la plataforma para lanzar un navegador web en una URL específica. Para cada plataforma, se prueban varios navegadores en un orden específico. El usuario puede modificar el navegador que se lanza estableciendo la variable de entorno BROWSER. (Originalmente inspirado por el parche de Eric S. Raymond a urllib que añadía una funcionalidad similar, pero el módulo final proviene de un código originalmente implementado por Fred Drake como Tools/idle/BrowserControl.py, y adaptado para la biblioteca estándar por Fred)

  • _winreg: Una interfaz para el registro de Windows. _winreg es una adaptación de las funciones que han formado parte de PythonWin desde 1995, pero ahora se ha añadido a la distribución principal, y se ha mejorado para soportar Unicode. _winreg fue escrito por Bill Tutt y Mark Hammond.

  • zipfile: Un módulo para leer y escribir archivos con formato ZIP. Se trata de archivos producidos por PKZIP en DOS/Windows o zip en Unix, que no deben confundirse con los archivos con formato gzip (que son compatibles con el módulo gzip) (Contribución de James C. Ahlstrom.)

  • imputil: Un módulo que proporciona una forma más sencilla de escribir ganchos de importación personalizados, en comparación con el módulo ihooks existente. (Implementado por Greg Stein, con mucha discusión en python-dev a lo largo del camino)

Mejoras en IDLE

IDLE es el IDE oficial de Python multiplataforma, escrito con Tkinter. Python 2.0 incluye IDLE 0.6, que añade una serie de nuevas características y mejoras. Una lista parcial:

  • Mejoras y optimizaciones de la interfaz de usuario, especialmente en el área de resaltado de sintaxis y auto-indentación.

  • El navegador de clases muestra ahora más información, como las funciones de nivel superior de un módulo.

  • El ancho del tabulador es ahora una opción configurable por el usuario. Al abrir un archivo Python existente, IDLE detecta automáticamente las convenciones de sangría y se adapta.

  • Ahora hay soporte para llamar a los navegadores en varias plataformas, utilizado para abrir la documentación de Python en un navegador.

  • IDLE ahora tiene una línea de comandos, que es en gran medida similar al intérprete de Python vainilla.

  • Se añadieron consejos de llamada en muchos lugares.

  • Ahora IDLE puede instalarse como un paquete.

  • En la ventana del editor, ahora hay una barra de líneas/columnas en la parte inferior.

  • Tres nuevos comandos de teclado: Comprobar módulo (Alt-F5), Importar módulo (F5) y Ejecutar script (Ctrl-F5).

Módulos eliminados y obsoletos

Se han eliminado algunos módulos porque son obsoletos, o porque ahora hay mejores formas de hacer lo mismo. El módulo stdwin ha desaparecido; era para un conjunto de herramientas de ventanas independientes de la plataforma que ya no se desarrolla.

Varios módulos han sido trasladados al subdirectorio lib-old: cmp, cmpcache, dircmp, dump, find, grep, packmail, poly, util, whatsound, zmod. Si tiene código que depende de un módulo que ha sido movido a lib-old, puede simplemente añadir ese directorio a sys.path para recuperarlo, pero se recomienda actualizar cualquier código que utilice estos módulos.

Agradecimientos

Los autores desean agradecer a las siguientes personas sus sugerencias sobre varios borradores de este artículo: David Bolen, Mark Hammond, Gregg Hauser, Jeremy Hylton, Fredrik Lundh, Detlef Lannert, Aahz Maruch, Skip Montanaro, Vladimir Marangozov, Tobias Polzin, Guido van Rossum, Neil Schemenauer y Russ Schmidt.