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.

Python's development never completely stops between releases, and a
steady flow of bug fixes and improvements are always being submitted.
A host of minor fixes, a few optimizations, additional docstrings, and
better error messages went into 2.0; to list them all would be
impossible, but they're certainly significant. Consult the publicly
available CVS logs if you want to see the full list.  This progress is
due to the five developers working for  PythonLabs are now getting
paid to spend their days fixing bugs, and also due to the improved
communication resulting  from moving to 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.

Entonces, ¿deberías interesarte mucho por Python 1.6?  Probablemente
no. Las versiones 1.6final y 2.0beta1 se publicaron el mismo día (5 de
septiembre de 2000), y el plan es finalizar Python 2.0 en un mes más o
menos. Si tienes aplicaciones que mantener, no parece que tenga mucho
sentido romper cosas al pasar a la 1.6, arreglarlas, y luego tener
otra ronda de roturas dentro de un mes al pasar a la 2.0; es mejor
pasar directamente a la 2.0. La mayoría de las características
realmente interesantes descritas en este documento sólo están en la
2.0, porque se hizo mucho trabajo entre mayo y septiembre.


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.

La facilidad para añadir código provocó algunos problemas iniciales de
crecimiento, como el hecho de que el código se registrara antes de
estar listo o sin obtener un acuerdo claro del grupo de
desarrolladores. El proceso de aprobación que ha surgido es algo
similar al utilizado por el grupo Apache. Los desarrolladores pueden
votar +1, +0, -0 o -1 sobre un parche; +1 y -1 denotan aceptación o
rechazo, mientras que +0 y -0 significan que el desarrollador es
mayormente indiferente al cambio, aunque con un ligero sesgo positivo
o negativo. El cambio más significativo con respecto al modelo de
Apache es que la votación es esencialmente consultiva, lo que permite
a Guido van Rossum, que tiene el estatus de Dictador Benevolente
Vitalicio, saber cuál es la opinión general. Puede seguir ignorando el
resultado de una votación y aprobar o rechazar un cambio aunque la
comunidad no esté de acuerdo con él.

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 "for"..."in" 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 "for"..."in", 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.

Work has been done on porting Python to 64-bit Windows on the Itanium
processor, mostly by Trent Mick of ActiveState.  (Confusingly,
"sys.platform" is still "'win32'" on Win64 because it seems that for
ease of porting, MS Visual C++ treats code as 32 bit on Itanium.)
PythonWin also supports Windows CE; see the Python CE page at
https://pythonce.sourceforge.net/ for more information.

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.
