# Qué hay de nuevo en Python 2.2

Autor

  A.M. Kuchling

## Introducción

Este artículo explica las nuevas características en Python 2.2.2, publicado el 14 de octubre de 2002. Python 2.2.2 es una versión de corrección de errores de Python 2.2, lanzada originalmente el 21 de diciembre de 2001.

Python 2.2 se puede considerar como la «versión de limpieza». Hay algunas características como los generadores e iteradores que son completamente nuevas, pero la mayoría de los cambios, aunque sean significativos y de gran alcance, tienen como objetivo limpiar las irregularidades y los rincones oscuros del diseño del lenguaje.

Este artículo no procura proporcionar una especificación completa de las nuevas características, pero en su lugar proporciona una descripción general conveniente. Para más detalles, deberías consultar la documentación de Python 2.2, como Python Library Reference y Python Reference Manual. Si quieres comprender la justificación completa de la implementación y el diseño de un cambio, consultar la PEP para conocer una característica nueva en particular.

## PEPs 252 y 253: Cambios de tipo y clase

Los cambios más grandes y de mayor alcance en Python 2.2 son el modelo de objetos y clases de Python. Los cambios deben ser compatibles con versiones anteriores, por lo que es probable que tu código continuará ejecutándose sin cambios, pero los cambios proporcionan algunas capacidades nuevas increíbles. Antes de comenzar esta, la sección más larga y complicada de este artículo, brindaré una descripción general de los cambios y ofreceré algunos comentarios.

Hace mucho tiempo escribí una página web que enumeraba los defectos en el diseño de Python. Una de las fallas más importantes fue que es imposible subclasificar tipos de Python implementados en C. En particular, no es posible subclasificar tipos incorporados, por lo que no se puede solo subclasificar, digamos, listas para agregar un solo método útil para ellos. El módulo `UserList` proporciona una clase que admite todos los métodos de listas y que puede subclasificarse aún más, pero hay mucho código C que espera una lista normal de Python y no aceptará una instancia `UserList`.

Python 2.2 corrige esto y, en el proceso, agrega algunas capacidades nuevas interesantes. Un breve resumen:

- Puedes subclasificar tipos incorporados como listas e incluso enteros, y tus subclases deberían funcionar en todos los lugares que requieran el tipo original.

- Ahora es posible definir métodos estáticos y de clase, además de los métodos de instancia disponibles en versiones anteriores de Python.

- También es posible llamar automáticamente métodos al acceder o configurar un atributo de instancia mediante el uso de un nuevo mecanismo llamado properties. Muchos usos de `__getattr__()` se pueden reescribir para usar propiedades en su lugar, haciendo que el código resultante sea más simple y rápido. Como un pequeño beneficio secundario, ahora también los atributos pueden tener docstrings.

- La lista de atributos legales para una instancia se puede limitar a un conjunto particular usando slots, lo que hace posible protegerse contra errores tipográficos y quizás hacer posibles más optimizaciones en versiones futuras de Python.

Algunos usuarios han expresado preocupación por todos estos cambios. Claro, dicen, las nuevas características son ordenadas y se prestan a todo tipo de trucos que no eran posibles en versiones anteriores de Python, pero también hacen que el lenguaje sea más complicado. Algunas personas han dicho que siempre han recomendado Python por su simplicidad, y sienten que su simplicidad se está perdiendo.

Personalmente. pienso que no hay que preocuparse. Muchas de las nuevas características son bastante esotéricas, y puedes escribir mucho código de Python sin tener que estar al tanto de ellas. Escribir una clase simple no es más difícil de lo que nunca fue, así que no necesitas molestarte en aprender o enseñarlos a menos que realmente sean necesarios. Algunas tareas muy complicadas que antes solo eran posibles desde C ahora serán posibles en Python puro, y en mi opinión, esto es todo para mejor.

No voy a intentar cubrir todas los casos de las esquinas y los pequeños cambios que fueron necesarios para hacer que las nuevas características funcionen. En su lugar, esta sección pintará solo a grandes rasgos. Consultar la sección Enlaces relacionados, «Enlaces relacionados», para más fuentes de información sobre el nuevo modelo de objetos de Python 2.2.

### Clases antiguas y nuevas

Primero, debes saber que realmente Python 2.2 tiene dos tipos de clases: clases clásicas o de estilo antiguo y clases de estilo nuevo. El modelo de clase de estilo antiguo exactamente es el mismo que el modelo de clase en versiones anteriores de Python. Todas las nuevas características descritas en esta sección se aplican solo a las clases de estilo nuevo. Esta divergencia no está destinada a durar para siempre; eventualmente las clases de estilo antiguo se eliminarán, posiblemente en Python 3.0.

Entonces, ¿cómo defines una clase de estilo nuevo? Lo haces subclasificando una clases de estilo nuevo existente. La mayoría de los tipos integrados de Python, como enteros, listas, diccionarios e incluso archivos, ahora son clases de estilo nuevo. También se agregó una clase de estilo nuevo llamada `object`, la clase base para todos los tipos integrados, por lo que si ningún tipo integrado es apropiado, puedes solo subclasificar `object`:

    class C(object):
        def __init__ (self):
            ...
        ...

Esto significa que las declaraciones `class` que no tienen ninguna clase base siempre son clases clásicas en Python 2.2. (Realmente también puedes cambiar esto configurando una variable de nivel de módulo llamada `__metaclass__` — consultar PEP 253 para más detalles — pero es más fácil solo subclasificar `object`.)

Los objetos de tipo para los tipos integrados están disponibles como incorporados, nombrados mediante un truco inteligente. Python siempre ha tenido funciones incorporadas llamadas `int()`, `float()` y `str()`. En la versión 2.2, ya no son funciones, sino objetos de tipo que se comportan como fábricas cuando se les llaman.

    >>> int
    <type 'int'>
    >>> int('123')
    123

Para completar el conjunto de tipos, se agregaron nuevos objetos de tipo como `dict()` y `file()`. Aquí hay un ejemplo más interesante, agregando un método `lock()` a los objetos de archivo:

    class LockableFile(file):
        def lock (self, operation, length=0, start=0, whence=0):
            import fcntl
            return fcntl.lockf(self.fileno(), operation,
                               length, start, whence)

El módulo ahora obsoleto `posixfile` contenía una clase que emulaba todos los métodos de un objeto de archivo y también agregaba un método `lock()`, pero esta clase no podía pasarse a funciones internas que esperaban un archivo incorporado, algo que es posible con nuestra nueva clase `LockableFile`.

### Descriptores

En versiones anteriores de Python, no había una forma consistente de descubrir qué atributos y métodos eran compatibles con un objeto. Había algunas convenciones informales, como definir atributos `__members__` y `__methods__` que eran listas de nombres, pero a menudo el autor de un tipo de extensión o una clase no se molestaría en definirlos. Podrías recurrir a inspeccionar el `__dict__` de un objeto, pero cuando la herencia de una clase o un gancho arbitrario `__getattr__()` estuvieran en uso, esto podría ser inexacto.

La única gran idea que subyace al nuevo modelo de clases es que se ha formalizado una API para describir los atributos de un objeto usando descriptors. Los descriptores especifican el valor de un atributo, indicando si es un método o un campo. Con la API de un descriptor, los métodos estáticos y de clase se vuelven posibles, así como constructos más exóticos.

Los descriptores de atributos son objetos que viven dentro de los objetos de clase y tienen algunos atributos propios:

- `__name__` es el nombre del atributo.

- `__doc__` es el docstring del atributo.

- `__get__(object)` es un método que recupera el valor del atributo de object.

- `__set__(object, value)` establece el atributo de object en value.

- `__delete__(object, value)` elimina el atributo value de object.

Por ejemplo, cuando escribes `obj.x`, los pasos que realmente Python realiza son:

    descriptor = obj.__class__.x
    descriptor.__get__(obj)

Para los métodos, `descriptor.__get__()` retorna un objeto temporal que se puede llamar y contiene la instancia y el método que se llamará en él. También esto es el por qué los métodos estáticos y de clase ahora son posibles; tienen descriptores que contienen solo el método o el método y la clase. Como una breve explicación de estos tipos nuevos de métodos, los métodos estáticos no se pasan a la instancia y, por lo tanto, se asemejan a funciones regulares. Los métodos de clase se pasan a la clase del objeto, pero no al objeto en sí. Los métodos estáticos y de clase se definen así:

    class C(object):
        def f(arg1, arg2):
            ...
        f = staticmethod(f)

        def g(cls, arg1, arg2):
            ...
        g = classmethod(g)

La función `staticmethod()` toma la función `f()` y la retorna en un descriptor para que pueda almacenarse en el objeto de clase. Puedes esperar que haya una sintaxis especial para crear tales métodos (`def static f`, `defstatic f()` o algo así) pero aún no se ha definido dicha sintaxis; que se ha dejado para versiones futuras de Python.

También se implementan más características nuevas, como ranuras y propiedades, como nuevos tipos de descriptores, y no es difícil escribir una clase de descriptor que haga algo nuevo. Por ejemplo, sería posible escribir una clase de descriptor que hiciera posible escribir condiciones previas al estilo Eiffel y posteriores para un método. Una clase que usó esta característica podría definirse así:

    from eiffel import eiffelmethod

    class C(object):
        def f(self, arg1, arg2):
            # The actual function
            ...
        def pre_f(self):
            # Check preconditions
            ...
        def post_f(self):
            # Check postconditions
            ...

        f = eiffelmethod(f, pre_f, post_f)

Toma en cuenta que una persona que usa la nueva función `eiffelmethod()` no tiene que entender nada sobre descriptores. Esta es la razón por la que creo que las nuevas características no incrementan la complejidad básica del lenguaje. Habrá algunos asistentes que necesitarán conocerlo para escribir `eiffelmethod()` o la ZODB o lo que sea, pero la mayoría de los usuarios solo escribirán código sobre las bibliotecas resultantes e ignorarán los detalles de implementación.

### Herencia múltiple: la regla del diamante

La herencia múltiple también se ha hecho más útil al cambiar las reglas bajo las cuales se resuelven los nombres. Considera este conjunto de clases (diagrama tomado de PEP 253 de Guido van Rossum):

          class A:
            ^ ^  def save(self): ...
           /   \
          /     \
         /       \
        /         \
    class B     class C:
        ^         ^  def save(self): ...
         \       /
          \     /
           \   /
            \ /
          class D

La regla de búsqueda para clases clásicas es simple pero no muy inteligente; se buscan las clases base primero en profundidad, yendo de izquierda a derecha. Una referencia a `D.save()` buscará las clases `D`, `B` y luego `A`, donde `save()` se encontraría y retornaría. `C.save()` nunca se encontraría en absoluto. Esto es malo, porque si el método `save()` de `C` está guardando algún estado interno específico de `C`, no llamarlo resultará en que este estado nunca se guardará.

Las clases de estilo nuevo siguen un algoritmo diferente que es más complicado de explicar, pero hace lo correcto en esta situación. (Toma en cuenta que Python 2.3 cambia este algoritmo a uno que produce los mismos resultados en la mayoría de los casos, pero produce resultados más útiles para gráficos de herencia realmente complicados.)

1. Enumera todas las clases base, siguiendo la regla de búsqueda clásica e incluye una clase varias veces si se visita repetidamente. En el ejemplo anterior, la lista de clases visitadas es [`D`, `B`, `A`, `C`, `A`].

2. Escanea la lista en busca de clases duplicadas. Si encuentra alguna, elimina todas menos una, dejando la última en la lista. En el ejemplo anterior, la lista se convierte en [`D`, `B`, `C`, `A`] después de eliminar las duplicadas.

Siguiendo esta regla, refiriéndose a `D.save()` retornará `C.save()`, el cual es el comportamiento que buscamos. Esta regla de búsqueda es la misma que sigue Common Lisp. Una nueva función incorporada, `super()`, proporciona una forma de acceder a las superclases de una clase sin tener que volver a implementar el algoritmo de Python. La forma más utilizada será `super(class, obj)`, la cual retorna un objeto de superclase vinculado. Esta forma se usará en métodos para llamar a un método en la superclase; por ejemplo, el método `save()` de `D` se vería así:

    class D (B,C):
        def save (self):
            # Call superclass .save()
            super(D, self).save()
            # Save D's private information here
            ...

También `super()` puede retornar objetos de superclase no vinculados cuando se llama como `super(class)` o `super(class1, class2)`, pero probablemente esto no sea útil a menudo.

### Acceso a atributos

Un buen número de clases sofisticadas de Python definen ganchos para el acceso de atributos usando `__getattr__()`; más comúnmente, esto se hace por conveniencia, para hacer que el código sea más legible al mapear automáticamente un acceso de atributo como `obj.parent` en una llamada de método como `obj.get_parent`. Python 2.2 agrega algunas formas nuevas de controlar el acceso de atributos.

Primero, `__getattr__(attr_name)` aún es compatible con las clases de nuevo estilo, y nada al respecto ha cambiado. Como antes, se llamará cuando se intente acceder a `obj.foo` y no se encuentre ningún atributo llamado `foo` en el diccionario de la instancia.

Las clases de nuevo estilo también admiten un nuevo método, `__getattribute__(attr_name)`. La diferencia entre los dos métodos es que `__getattribute__()` siempre se llama cada vez que se accede a cualquier atributo, mientras que el antiguo `__getattr__()` se llama solo si no se encuentra `foo` en el diccionario de la instancia.

Sin embargo, el soporte de Python 2.2 para properties será a menudo una forma más simple de atrapar referencias de atributos. Escribir un método `__getattr__()` es complicado porque para evitar la recursividad no puedes usar accesos regulares a atributos dentro de ellos, y en su lugar tienes que jugar con el contenido de `__dict__`. Los métodos `__getattr__()` también terminan siendo llamados por Python cuando busca otros métodos como `__repr__()` o `__coerce__()`, por lo que se tienen que escribirse teniendo esto en cuenta. Finalmente, llamar una función en cada acceso de atributo resulta en una pérdida de rendimiento considerable.

`property` es un nuevo tipo integrado que empaqueta tres funciones obtienen, establecen o eliminan un atributo y una docstring. Por ejemplo, si quieres definir un atributo `size` que se calcula, pero que también se puede configurar, puedes escribir:

    class C(object):
        def get_size (self):
            result = ... computation ...
            return result
        def set_size (self, size):
            ... compute something based on the size
            and set internal state appropriately ...

        # Define a property.  The 'delete this attribute'
        # method is defined as None, so the attribute
        # can't be deleted.
        size = property(get_size, set_size,
                        None,
                        "Storage size of this instance")

Eso es ciertamente más claro y fácil de escribir que un par de métodos `__getattr__()`/`__setattr__()` que verifican el atributo `size` y lo manejan especialmente mientras recuperan todos los demás atributos `__dict__` de la instancia. Los accesos a `size` también son los únicos que tienen que realizar el trabajo de llamar a una función, por lo que las referencias a otros atributos se ejecutan a su velocidad habitual.

Finalmente, es posible restringir la lista de atributos que se pueden referenciar en un objeto usando el nuevo atributo de clase `__slots__`. Los objetos de Python por lo general son muy dinámicos; en cualquier momento es posible definir un nuevo atributo en una instancia haciendo simplemente `obj.new_attr=1`. Una clase de estilo nuevo puede definir un atributo de clase llamado `__slots__` para limitar los atributos legales a un conjunto particular de nombres. Un ejemplo hará esto claro:

    >>> class C(object):
    ...     __slots__ = ('template', 'name')
    ...
    >>> obj = C()
    >>> print obj.template
    None
    >>> obj.template = 'Test'
    >>> print obj.template
    Test
    >>> obj.newattr = None
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
    AttributeError: 'C' object has no attribute 'newattr'

Toma en cuenta cómo obtienes un `AttributeError` en el intento de asignar a un atributo que no aparece en `__slots__`.

### Enlaces relacionados

Esta sección solo ha sido una descripción rápida de las nuevas características, brindando una explicación suficiente para comenzar a programar, pero muchos detalles se han simplificado o ignorado. ¿Dónde deberías ir para obtener una imagen más completa?

The Guía práctica de uso de los descriptores is a lengthy tutorial introduction to the descriptor features, written by Guido van Rossum. If my description has whetted your appetite, go read this tutorial next, because it goes into much more detail about the new features while still remaining quite easy to read.

A continuación, hay dos PEPs relevantes, PEP 252 y PEP 253. PEP 252 se titula «Hacer que los tipos se parezcan más a las clases», y cubre la API del descriptor. PEP 253 se titula «Subtipado de tipos incorporados», y describe los cambios de los objetos de tipo que hacen posible el subtipo de objetos incorporados. PEP 253 es la PEP más complicada de las dos, y en algunos puntos las explicaciones necesarias de tipos y meta-tipos pueden causar que tu cabeza explote. Ambas PEPs se escribieron e implementaron por Guido van Rossum con la asistencia sustancial del resto del equipo de Zope Corp.

Finalmente, está la máxima autoridad: el código fuente. La mayoría de la maquinaria para el manejo de tipos está en `Objects/typeobject.c`, pero solo debes recurrir a él después de que se hayan agotado todas las demás vías, incluida la publicación de una pregunta en python-list o python-dev.

## PEP 234: Iteradores

Otra adición significativa a la versión 2.2 es una interfaz de iteración tanto a nivel de C como de Python. Los objetos pueden definir cómo pueden ser iterados por quienes los llaman.

En las versiones de Python hasta la 2.1, la forma habitual de hacer que funcione `for item in obj` es definir un método `__getitem__()` que se parezca a esto:

    def __getitem__(self, index):
        return <next item>

`__getitem__()` se utiliza más adecuadamente para definir una operación de indexación en un objeto, de modo que se puede escribir `obj[5]` para recuperar el sexto elemento. Es un poco engañoso cuando se utiliza esto sólo para soportar bucles `for`. Considere algún objeto tipo archivo que quiera ser revisado en bucle; el parámetro index no tiene sentido, ya que la clase probablemente asume que se hará una serie de llamadas a `__getitem__()` con index incrementándose en uno cada vez. En otras palabras, la presencia del método `__getitem__()` no significa que el uso de `file[5]` para acceder al azar al sexto elemento vaya a funcionar, aunque realmente debería hacerlo.

En Python 2.2, la iteración puede implementarse por separado, y los métodos `__getitem__()` pueden limitarse a las clases que realmente soportan el acceso aleatorio. La idea básica de los iteradores es simple. Una nueva función incorporada, `iter(obj)` o `iter(C, sentinel)`, se utiliza para obtener un iterador. `iter(obj)` retorna un iterador para el objeto obj, mientras que `iter(C, sentinel)` retorna un iterador que invocará al objeto invocable C hasta que retorne sentinel para señalar que el iterador ha terminado.

Las clases de Python pueden definir un método `__iter__()`, que debe crear y retornar un nuevo iterador para el objeto; si el objeto es su propio iterador, este método puede simplemente retornar `self`. En particular, los iteradores suelen ser sus propios iteradores. Los tipos de extensión implementados en C pueden implementar una función `tp_iter` para retornar un iterador, y los tipos de extensión que quieran comportarse como iteradores pueden definir una función `tp_iternext`.

Entonces, después de todo esto, ¿qué hacen realmente los iteradores? Tienen un método obligatorio, `next()`, que no toma argumentos y retorna el siguiente valor. Cuando no hay más valores que retornar, la llamada a `next()` debería lanzar la excepción `StopIteration`.

    >>> L = [1,2,3]
    >>> i = iter(L)
    >>> print i
    <iterator object at 0x8116870>
    >>> i.next()
    1
    >>> i.next()
    2
    >>> i.next()
    3
    >>> i.next()
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
    StopIteration
    >>>

En 2.2, la sentencia `for` de Python ya no espera una secuencia; espera algo para lo que `iter()` retornará un iterador. Por compatibilidad y comodidad, se construye automáticamente un iterador para las secuencias que no implementan `__iter__()` o una ranura `tp_iter`, por lo que `for i in [1,2,3]` seguirá funcionando. Dondequiera que el intérprete de Python haga un bucle sobre una secuencia, se ha cambiado para utilizar el protocolo de los iteradores. Esto significa que puedes hacer cosas como esta:

    >>> L = [1,2,3]
    >>> i = iter(L)
    >>> a,b,c = i
    >>> a,b,c
    (1, 2, 3)

Se ha añadido soporte de iteradores a algunos de los tipos básicos de Python. Llamar a `iter()` sobre un diccionario retornará un iterador que hace un bucle sobre sus claves:

    >>> m = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
    ...      'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
    >>> for key in m: print key, m[key]
    ...
    Mar 3
    Feb 2
    Aug 8
    Sep 9
    May 5
    Jun 6
    Jul 7
    Jan 1
    Apr 4
    Nov 11
    Dec 12
    Oct 10

Este es el comportamiento por defecto. Si quieres iterar sobre claves, valores o pares clave/valor, puedes llamar explícitamente a los métodos `iterkeys()`, `itervalues()` o `iteritems()` para obtener un iterador apropiado. En un cambio menor relacionado, el operador `in` ahora funciona en los diccionarios, por lo que `key in dict` es ahora equivalente a `dict.has_key(key)`.

Los archivos también proporcionan un iterador, que llama al método `readline()` hasta que no hay más líneas en el archivo. Esto significa que ahora puede leer cada línea de un archivo utilizando un código como este:

    for line in file:
        # do something for each line
        ...

Tenga en cuenta que sólo puede avanzar en un iterador; no hay forma de obtener el elemento anterior, reiniciar el iterador o hacer una copia del mismo. Un objeto iterador podría proporcionar estas capacidades adicionales, pero el protocolo iterador sólo requiere un método `next()`.

Ver también

PEP 234 - Iteradores

  Escrito por Ka-Ping Yee y GvR; implementado por el equipo de Python Labs, principalmente por GvR y Tim Peters.

## PEP 255: Generadores simples

Los generadores son otra novedad que interactúa con la introducción de los iteradores.

Sin duda estás familiarizado con cómo funcionan las llamadas a funciones en Python o C. Cuando llamas a una función, ésta obtiene un espacio de nombres privado donde se crean sus variables locales. Cuando la función llega a una declaración `return`, las variables locales se destruyen y el valor resultante se retorna a quien la llamó. Una llamada posterior a la misma función obtendrá un nuevo conjunto de variables locales. Pero, ¿qué pasaría si las variables locales no se tiraran al salir de una función? ¿Qué pasaría si pudieras reanudar la función donde la dejaste? Esto es lo que proporcionan los generadores; se puede pensar en ellos como funciones reanudables.

Este es el ejemplo más sencillo de una función generadora:

    def generate_ints(N):
        for i in range(N):
            yield i

Se ha introducido una nueva palabra clave, `yield`, para los generadores. Cualquier función que contenga una declaración `yield` es una función generadora; esto es detectado por el compilador de código de bits de Python que compila la función especialmente como resultado. Debido a la introducción de una nueva palabra clave, los generadores deben ser explícitamente habilitados en un módulo incluyendo una declaración `from __future__ import generators` cerca de la parte superior del código fuente del módulo. En Python 2.3 esta declaración será innecesaria.

Cuando se llama a una función generadora, ésta no retorna un único valor, sino que retorna un objeto generador que soporta el protocolo de los iteradores. Al ejecutar la sentencia `yield`, el generador retorna el valor de `i`, de forma similar a una sentencia `return`. La gran diferencia entre `yield` y una sentencia `return` es que al llegar a una sentencia `yield` se suspende el estado de ejecución del generador y se conservan las variables locales. En la siguiente llamada al método `next()` del generador, la función se reanudará la ejecución inmediatamente después de la sentencia `yield`. (Por razones complicadas, la sentencia `yield` no está permitida dentro del bloque `try` de una sentencia `try`…``finally`; lea PEP 255 para una explicación completa de la interacción entre `yield` y las excepciones)

Este es un ejemplo de uso del generador `generate_ints()`:

    >>> gen = generate_ints(3)
    >>> gen
    <generator object at 0x8117f90>
    >>> gen.next()
    0
    >>> gen.next()
    1
    >>> gen.next()
    2
    >>> gen.next()
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
      File "<stdin>", line 2, in generate_ints
    StopIteration

También podrías escribir `for i in generate_ints(5)`, o `a,b,c = generate_ints(3)`.

Dentro de una función generadora, la sentencia `return` sólo puede usarse sin un valor, y señala el final de la procesión de valores; después el generador no puede retornar más valores. `return` con un valor, como `return 5`, es un error de sintaxis dentro de una función generadora. El final de los resultados del generador también puede indicarse levantando manualmente `StopIteration`, o simplemente dejando que el flujo de ejecución caiga en el fondo de la función.

Puedes conseguir el efecto de los generadores manualmente escribiendo tu propia clase y almacenando todas las variables locales del generador como variables de instancia. Por ejemplo, la lución de una lista de enteros podría hacerse estableciendo `self.count` a 0, y haciendo que el método `next()` incremente `self.count` y lo retorne. Sin embargo, para un generador medianamente complicado, escribir la clase correspondiente sería mucho más complicado. `Lib/test/test_generators.py` contiene varios ejemplos más interesantes. El más sencillo implementa un recorrido en orden de un árbol utilizando generadores de forma recursiva

    # A recursive generator that generates Tree leaves in in-order.
    def inorder(t):
        if t:
            for x in inorder(t.left):
                yield x
            yield t.label
            for x in inorder(t.right):
                yield x

Otros dos ejemplos en `Lib/test/test_generators.py` producen soluciones para el problema de las N reinas (colocar $N$ reinas en un tablero de ajedrez $NxN$ de forma que ninguna reina amenace a otra) y el recorrido del caballero (una ruta que lleva a un caballo a cada casilla de un tablero de ajedrez $NxN$ sin visitar ninguna casilla dos veces).

La idea de los generadores proviene de otros lenguajes de programación, especialmente de Icon (https://www.cs.arizona.edu/icon/), donde la idea de los generadores es fundamental. En Icon, cada expresión y llamada a una función se comporta como un generador. Un ejemplo de «An Overview of the Icon Programming Language» en https://www.cs.arizona.edu/icon/docs/ipd266.htm da una idea de cómo es esto:

    sentence := "Store it in the neighboring harbor"
    if (i := find("or", sentence)) > 5 then write(i)

En Icon la función `find()` retorna los índices en los que se encuentra la subcadena «o»: 3, 23, 33. En la sentencia `if`, a `i` se le asigna primero un valor de 3, pero 3 es menor que 5, por lo que la comparación falla, e Icon la reintenta con el segundo valor de 23. 23 es mayor que 5, por lo que la comparación ahora tiene éxito, y el código imprime el valor 23 en la pantalla.

Python no va tan lejos como Icon en la adopción de generadores como concepto central. Los generadores se consideran una nueva parte del núcleo del lenguaje Python, pero aprenderlos o utilizarlos no es obligatorio; si no resuelven ningún problema que tengas, siéntete libre de ignorarlos. Una característica novedosa de la interfaz de Python en comparación con la de Icon es que el estado de un generador se representa como un objeto concreto (el iterador) que puede pasarse a otras funciones o almacenarse en una estructura de datos.

Ver también

PEP 255 - Generadores simples

  Escrito por Neil Schemenauer, Tim Peters, Magnus Lie Hetland. Implementado principalmente por Neil Schemenauer y Tim Peters, con otras correcciones del equipo de Python Labs.

## PEP 237: Unificación de enteros largos y enteros

En versiones recientes, la distinción entre enteros regulares, que son valores de 32 bits en la mayoría de las máquinas, y enteros largos, que pueden tener un tamaño arbitrario, se estaba convirtiendo en una molestia. Por ejemplo, en las plataformas que soportan archivos de más de `2**32` bytes, el método `tell()` de los objetos archivo tiene que retornar un entero largo. Sin embargo, había varias partes de Python que esperaban números enteros simples y que daban un error si se proporcionaba un número entero largo en su lugar. Por ejemplo, en Python 1.5, sólo podían usarse enteros normales como índice de corte, y `'abc'[1L:]` lanzaba una excepción `TypeError` con el mensaje “slice index must be int”.

Python 2.2 cambiará los valores de enteros cortos a enteros largos según sea necesario. El sufijo “L” ya no es necesario para indicar un literal entero largo, ya que ahora el compilador elegirá el tipo apropiado. (El uso del sufijo “L” se desaconsejará en futuras versiones 2.x de Python, provocando una advertencia en Python 2.4, y probablemente se eliminará en Python 3.0) Muchas operaciones que solían lanzar un `OverflowError` ahora retornarán un entero largo como resultado. Por ejemplo:

    >>> 1234567890123
    1234567890123L
    >>> 2 ** 64
    18446744073709551616L

En la mayoría de los casos, los enteros y los enteros largos se tratarán ahora de forma idéntica. Todavía se pueden distinguir con la función incorporada `type()`, pero rara vez se necesita.

Ver también

PEP 237 - Unificación de enteros largos y enteros

  Escrito por Moshe Zadka y Guido van Rossum. Implementado principalmente por Guido van Rossum.

## PEP 238: Cambio del operador de división

El cambio más controvertido de Python 2.2 anuncia el inicio de un esfuerzo por arreglar un viejo defecto de diseño que ha estado en Python desde el principio. Actualmente, el operador de división de Python, `/`, se comporta como el operador de división de C cuando se le presentan dos argumentos enteros: retorna un resultado entero que se trunca cuando hay una parte fraccionaria. Por ejemplo, `3/2` es 1, no 1,5, y `(-1)/2` es -1, no -0,5. Esto significa que los resultados de la división pueden variar inesperadamente dependiendo del tipo de los dos operandos y, como Python está tipado dinámicamente, puede ser difícil determinar los posibles tipos de los operandos.

(La controversia se centra en si esto es realmente un defecto de diseño, y si vale la pena romper el código existente para arreglarlo. Ha provocado interminables discusiones en python-dev, y en julio de 2001 estalló una tormenta de publicaciones ácidamente sarcásticas en comp.lang.python. No argumentaré aquí a favor de ninguno de los dos bandos y me limitaré a describir lo que se ha implementado en la 2.2. Lea PEP 238 para un resumen de los argumentos y contra-argumentos)

Debido a que este cambio podría romper el código, se está introduciendo de forma muy gradual. Python 2.2 comienza la transición, pero el cambio no será completo hasta Python 3.0.

En primer lugar, tomaré prestada alguna terminología de PEP 238. La «división verdadera» es la división con la que la mayoría de los no programadores están familiarizados: 3/2 es 1,5, 1/4 es 0,25, y así sucesivamente. La «división por el piso» es lo que hace actualmente el operador `/` de Python cuando se le dan operandos enteros; el resultado es el piso del valor retornado por la división verdadera. La «división clásica» es el comportamiento mixto actual de `/`; retorna el resultado de la división por el suelo cuando los operandos son enteros, y retorna el resultado de la división verdadera cuando uno de los operandos es un número de punto flotante.

Estos son los cambios que introduce la versión 2.2:

- Un nuevo operador, `//`, es el operador de división por el suelo. (Sí, ya sabemos que se parece al símbolo de comentario de C++.) `//` siempre realiza la división por el suelo sin importar los tipos de sus operandos, así que `1 // 2` es 0 y `1.0 // 2.0` también es 0.0.

  `//` está siempre disponible en Python 2.2; no es necesario habilitarlo mediante una sentencia ``__future__`.

- Al incluir una declaración `from __future__ import division` en un módulo, el operador `/` se cambiará para retornar el resultado de la división verdadera, por lo que `1/2` es 0,5. Sin la declaración `__future__`, `/` sigue significando la división clásica. El significado por defecto de `/` no cambiará hasta Python 3.0.

- Las clases pueden definir métodos llamados `__truediv__()` y `__floordiv__()` para sobrecargar los dos operadores de división. En el nivel C, también hay ranuras en la estructura `PyNumberMethods` para que los tipos de extensión puedan definir los dos operadores.

- Python 2.2 admite algunos argumentos de línea de comandos para comprobar si el código funcionará con la semántica de división modificada. Ejecutar python con `-Q warn` hará que se emita una advertencia cada vez que se aplique la división a dos enteros. Puedes usar esto para encontrar el código que está afectado por el cambio y arreglarlo. Por defecto, Python 2.2 simplemente realizará la división clásica sin una advertencia; la advertencia se activará por defecto en Python 2.3.

Ver también

PEP 238 - Cambio del operador de división

  Escrito por Moshe Zadka y Guido van Rossum. Implementado por Guido van Rossum..

## Cambios en Unicode

El soporte de Unicode de Python se ha mejorado un poco en la versión 2.2. Las cadenas Unicode se almacenan normalmente como UCS-2, como enteros sin signo de 16 bits. Python 2.2 también puede ser compilado para usar UCS-4, enteros sin signo de 32 bits, como su codificación interna suministrando `--enable-unicode=ucs4` al script de configuración. (También es posible especificar `--disable-unicode` para desactivar completamente el soporte de Unicode)

Cuando se compila para usar UCS-4 (un «Python amplio»), el intérprete puede manejar de forma nativa caracteres Unicode desde U+000000 hasta U+110000, por lo que el rango de valores legales para la función `unichr()` se expande en consecuencia. Utilizando un intérprete compilado para usar UCS-2 (un «Python estrecho»), los valores mayores de 65535 seguirán provocando que `unichr()` lance una excepción `ValueError`. Todo esto se describe en PEP 261, «Soporte para caracteres Unicode “anchos”»; consúltelo para más detalles.

Otro cambio es más sencillo de explicar. Desde su introducción, las cadenas Unicode han soportado un método `encode()` para convertir la cadena a una codificación seleccionada como UTF-8 o Latin-1. Un método simétrico `decode([*encoding*])` ha sido añadido a las cadenas de 8 bits (aunque no a las cadenas Unicode) en 2.2. `decode()` asume que la cadena está en la codificación especificada y la decodifica, retornando lo que sea retornado por el códec.

Gracias a esta nueva función, se han añadido códecs para tareas no relacionadas directamente con Unicode. Por ejemplo, se han añadido códecs para la codificación uu, la codificación base64 de MIME y la compresión con el módulo `zlib`:

    >>> s = """Here is a lengthy piece of redundant, overly verbose,
    ... and repetitive text.
    ... """
    >>> data = s.encode('zlib')
    >>> data
    'x\x9c\r\xc9\xc1\r\x80 \x10\x04\xc0?Ul...'
    >>> data.decode('zlib')
    'Here is a lengthy piece of redundant, overly verbose,\nand repetitive text.\n'
    >>> print s.encode('uu')
    begin 666 <data>
    M2&5R92!I<R!A(&QE;F=T:'D@<&EE8V4@;V8@<F5D=6YD86YT+"!O=F5R;'D@
    >=F5R8F]S92P*86YD(')E<&5T:71I=F4@=&5X="X*

    end
    >>> "sheesh".encode('rot-13')
    'furrfu'

Para convertir una instancia de clase a Unicode, se puede definir un método `__unicode__()` por clase, análogo a `__str__()`.

`encode()`, `decode()`, y `__unicode__()` fueron implementados por Marc-André Lemburg. Los cambios para soportar el uso de UCS-4 internamente fueron implementados por Fredrik Lundh y Martin von Löwis.

Ver también

PEP 261 - Soporte para caracteres Unicode “anchos”

  Escrito por Paul Prescod.

## PEP 227: Ámbitos anidados

En Python 2.1, los ámbitos anidados estáticamente se añadieron como una característica opcional, que se activaba mediante una directiva `from __future__ import nested_scopes`. En 2.2 los ámbitos anidados ya no necesitan ser habilitados especialmente, y ahora están siempre presentes. El resto de esta sección es una copia de la descripción de los ámbitos anidados de mi documento «What’s New in Python 2.1»; si lo leíste cuando salió la 2.1, puedes saltarte el resto de esta sección.

El mayor cambio introducido en Python 2.1, y completado en 2.2, es el de las reglas de alcance de Python. En Python 2.0, en cualquier momento hay como máximo tres espacios de nombres utilizados para buscar nombres de variables: local, a nivel de módulo y el espacio de nombres incorporado. Esto a menudo sorprendía a la gente porque no coincidía con sus expectativas intuitivas. Por ejemplo, una definición de función recursiva anidada no funciona:

    def f():
        ...
        def g(value):
            ...
            return g(value-1) + 1
        ...

La función `g()` siempre lanzará una excepción `NameError`, porque el enlace del nombre `g` no está ni en su espacio de nombres local ni en el espacio de nombres a nivel de módulo. Esto no es un gran problema en la práctica (¿con qué frecuencia se definen recursivamente funciones interiores como ésta?), pero esto también hacía más torpe el uso de la expresión `lambda`, y esto era un problema en la práctica. En el código que utiliza `lambda` a menudo se pueden encontrar variables locales que se copian al pasarlas como valores por defecto de los argumentos.

    def find(self, name):
        "Return list of any entries equal to 'name'"
        L = filter(lambda x, name=name: x == name,
                   self.list_attribute)
        return L

La legibilidad del código Python escrito en un estilo fuertemente funcional sufre mucho como resultado.

El cambio más significativo de Python 2.2 es que se ha añadido al lenguaje el ámbito estático para solucionar este problema. Como primer efecto, el argumento por defecto `name=name` es ahora innecesario en el ejemplo anterior. En pocas palabras, cuando a un nombre de variable dado no se le asigna un valor dentro de una función (mediante una asignación, o las sentencias `def`, `class`, o `import`), las referencias a la variable se buscarán en el espacio de nombres local del ámbito que la rodea. Puede encontrar una explicación más detallada de las reglas y una disección de la implementación en el PEP.

Este cambio puede causar algunos problemas de compatibilidad para el código en el que el mismo nombre de variable se utiliza tanto a nivel de módulo como de variable local dentro de una función que contiene otras definiciones de función. Sin embargo, esto parece bastante improbable, ya que dicho código habría sido bastante confuso de leer en primer lugar.

Un efecto secundario del cambio es que las sentencias `from module import *` y `exec` se han hecho ilegales dentro del ámbito de una función bajo ciertas condiciones. El manual de referencia de Python ha dicho todo el tiempo que `from module import *` sólo es legal en el nivel superior de un módulo, pero el intérprete de CPython nunca ha aplicado esto antes. Como parte de la implementación de los ámbitos anidados, el compilador que convierte el código fuente de Python en bytecodes tiene que generar un código diferente para acceder a las variables de un ámbito contenedor. Los códigos `from module import *` y `exec` hacen que el compilador no pueda averiguar esto, porque añaden nombres al espacio de nombres local que son desconocidos en tiempo de compilación. Por lo tanto, si una función contiene definiciones de funciones o expresiones `lambda` con variables libres, el compilador lo señalará lanzando una excepción `SyntaxError`.

Para que la explicación anterior quede un poco más clara, he aquí un ejemplo:

    x = 1
    def f():
        # The next line is a syntax error
        exec 'x=2'
        def g():
            return x

La línea 4 que contiene la sentencia `exec` es un error de sintaxis, ya que `exec` definiría una nueva variable local llamada `x` cuyo valor debería ser accedido por `g()`.

Esto no debería ser una gran limitación, ya que `exec` rara vez se utiliza en la mayoría del código de Python (y cuando se utiliza, a menudo es un signo de un mal diseño de todos modos).

Ver también

PEP 227 - Ámbitos anidados estáticamente

  Escrito e implementado por Jeremy Hylton.

## Módulos nuevos y mejorados

- El módulo `xmlrpclib` fue aportado a la biblioteca estándar por Fredrik Lundh, proporcionando soporte para escribir clientes XML-RPC. XML-RPC es un sencillo protocolo de llamada a procedimientos remotos construido sobre HTTP y XML. Por ejemplo, el siguiente fragmento recupera una lista de canales RSS de la red O’Reilly y, a continuación, muestra los titulares recientes de un canal:

      import xmlrpclib
      s = xmlrpclib.Server(
            'http://www.oreillynet.com/meerkat/xml-rpc/server.php')
      channels = s.meerkat.getChannels()
      # channels is a list of dictionaries, like this:
      # [{'id': 4, 'title': 'Freshmeat Daily News'}
      #  {'id': 190, 'title': '32Bits Online'},
      #  {'id': 4549, 'title': '3DGamers'}, ... ]

      # Get the items for one channel
      items = s.meerkat.getItems( {'channel': 4} )

      # 'items' is another list of dictionaries, like this:
      # [{'link': 'http://freshmeat.net/releases/52719/',
      #   'description': 'A utility which converts HTML to XSL FO.',
      #   'title': 'html2fo 0.3 (Default)'}, ... ]

  El módulo `SimpleXMLRPCServer` facilita la creación de servidores XML-RPC sencillos. Consulte http://xmlrpc.scripting.com/ para obtener más información sobre XML-RPC.

- El nuevo módulo `hmac` implementa el algoritmo HMAC descrito por RFC 2104. (Contribución de Gerhard Häring)

- Varias funciones que originalmente retornaban tuplas largas ahora retornan pseudo-secuencias que siguen comportándose como tuplas pero que también tienen atributos mnemónicos como memberst_mtime o `tm_year`. Las funciones mejoradas incluyen `stat()`, `fstat()`, `statvfs()` y `fstatvfs()` en el módulo `os`, y `localtime()`, `gmtime()` y `strptime()` en el módulo `time`.

  Por ejemplo, para obtener el tamaño de un archivo utilizando las antiguas tuplas, se terminaba escribiendo algo como `tamaño_de_archivo = os.stat(nombre_de_archivo)[stat.ST_SIZE]`, pero ahora se puede escribir más claramente como `tamaño_de_archivo = os.stat(nombre_de_archivo).st_size`.

  El parche original para esta función fue aportado por Nick Mathewson.

- El perfilador de Python ha sido ampliamente revisado y se han corregido varios errores en su salida. (Contribución de Fred L. Drake, Jr. y Tim Peters)

- El módulo `socket` puede ser compilado para soportar IPv6; especifica la opción `--enable-ipv6` al script configure de Python. (Contribución de Jun-ichiro «itojun» Hagino)

- Two new format characters were added to the `struct` module for 64-bit integers on platforms that support the C `long long` type. `q` is for a signed 64-bit integer, and `Q` is for an unsigned one. The value is returned in Python’s long integer type. (Contributed by Tim Peters.)

- En el modo interactivo del intérprete, hay una nueva función incorporada `help()` que utiliza el módulo `pydoc` introducido en Python 2.1 para proporcionar ayuda interactiva. `help()` sin ningún argumento te sitúa en una utilidad de ayuda online, donde puedes introducir los nombres de las funciones, clases o módulos para leer su texto de ayuda. (Contribuido por Guido van Rossum, usando el módulo `pydoc` de Ka-Ping Yee)

- Se han realizado varias correcciones de errores y mejoras de rendimiento en el motor SRE subyacente al módulo `re`. Por ejemplo, las funciones `re.sub()` y `re.split()` han sido reescritas en C. Otro parche contribuido acelera ciertos rangos de caracteres Unicode por un factor de dos, y un nuevo método `finditer()` que retorna un iterador sobre todas las coincidencias no superpuestas en una cadena dada. (El mantenimiento de SRE corre a cargo de Fredrik Lundh. El parche BIGCHARSET fue aportado por Martin von Löwis)

- El módulo `smtplib` soporta ahora RFC 2487, «Secure SMTP over TLS», por lo que ahora es posible cifrar el tráfico SMTP entre un programa Python y el agente de transporte de correo que recibe un mensaje. `smtplib` también soporta la autenticación SMTP. (Contribución de Gerhard Häring)

- El módulo `imaplib`, mantenido por Piers Lauder, tiene soporte para varias extensiones nuevas: la extensión NAMESPACE definida en RFC 2342, SORT, GETACL y SETACL. (Contribución de Anthony Baxter y Michel Pelletier)

- El módulo `rfc822` que analiza las direcciones de correo electrónico cumple ahora con RFC 2822, una actualización de RFC 822. (El nombre del módulo no se va a cambiar a `rfc2822`.) También se ha añadido un nuevo paquete, `email`, para analizar y generar mensajes de correo electrónico. (Contribuido por Barry Warsaw, y surgido de su trabajo en Mailman)

- El módulo `difflib` contiene ahora una nueva clase `Differ` para producir listas legibles por humanos de cambios (un «delta») entre dos secuencias de líneas de texto. También hay dos funciones generadoras, `ndiff()` y `restore()`, que retornan respectivamente un delta de dos secuencias, o una de las secuencias originales de un delta. (Trabajo de gruñido contribuido por David Goodger, a partir del código ndiff.py de Tim Peters que luego hizo la generización)

- Se han añadido las nuevas constantes `ascii_letters`, `ascii_lowercase` y `ascii_uppercase` al módulo `string`. Había varios módulos en la biblioteca estándar que utilizaban `string.letters` para referirse a los rangos A-Za-z, pero esa suposición es incorrecta cuando se utilizan locales, porque `string.letters` varía dependiendo del conjunto de caracteres legales definidos por el local actual. Los módulos con errores se han corregido para que utilicen `ascii_letters` en su lugar. (Informado por una persona desconocida; corregido por Fred L. Drake, Jr.)

- El módulo `mimetypes` facilita ahora el uso de bases de datos de tipos MIME alternativos mediante la adición de una clase `MimeTypes`, que toma una lista de nombres de archivo para ser analizados. (Contribución de Fred L. Drake, Jr.)

- Se ha añadido una clase `Timer` al módulo `threading` que permite programar una actividad para que ocurra en algún momento futuro. (Contribución de Itamar Shtull-Trauring)

## Cambios y correcciones en el intérprete

Algunos de los cambios sólo afectan a la gente que trata con el intérprete de Python a nivel de C porque están escribiendo módulos de extensión de Python, incrustando el intérprete, o simplemente hackeando el propio intérprete. Si sólo escribes código Python, ninguno de los cambios descritos aquí te afectará mucho.

- Las funciones de perfilado y rastreo pueden implementarse ahora en C, que puede operar a velocidades mucho mayores que las funciones basadas en Python y debería reducir la sobrecarga de perfilado y rastreo. Esto será de interés para los autores de entornos de desarrollo para Python. Se han añadido dos nuevas funciones en C a la API de Python, `PyEval_SetProfile()` y `PyEval_SetTrace()`. Las funciones `sys.setprofile()` y `sys.settrace()` existentes siguen existiendo, y simplemente se han cambiado para utilizar la nueva interfaz de nivel C. (Contribución de Fred L. Drake, Jr.)

- Se ha añadido otra API de bajo nivel, principalmente de interés para los implementadores de depuradores y herramientas de desarrollo de Python. `PyInterpreterState_Head()` y `PyInterpreterState_Next()` permiten al usuario recorrer todos los objetos intérpretes existentes; `PyInterpreterState_ThreadHead()` y `PyThreadState_Next()` permiten recorrer todos los estados de los hilos de un intérprete dado. (Contribución de David Beazley)

- La interfaz a nivel de C para el recolector de basura ha sido cambiada para facilitar la escritura de tipos de extensión que soporten la recolección de basura y para depurar los malos usos de las funciones. Varias funciones tienen una semántica ligeramente diferente, por lo que hubo que cambiar el nombre de un montón de funciones. Las extensiones que utilizan la antigua API seguirán compilando pero no participarán en la recolección de basura, por lo que actualizarlas para la 2.2 debería considerarse de alta prioridad.

  Para actualizar un módulo de extensión a la nueva API, realice los siguientes pasos:

- Cambia el nombre de `Py_TPFLAGS_GC()` a `PyTPFLAGS_HAVE_GC()`.

- Utilice `PyObject_GC_New()` o `PyObject_GC_NewVar()` para asignar

    objetos, y `PyObject_GC_Del()` para desocuparlos.

- Cambiar el nombre de `PyObject_GC_Init()` a `PyObject_GC_Track()` y

    `PyObject_GC_Fini()` a `PyObject_GC_UnTrack()`.

- Eliminar `PyGC_HEAD_SIZE()` del cálculo del tamaño de los objetos.

- Eliminar las llamadas a `PyObject_AS_GC()` y `PyObject_FROM_GC()`.

- Se ha añadido una nueva secuencia de formato `et` a `PyArg_ParseTuple()`; `et` toma tanto un parámetro como un nombre de codificación, y convierte el parámetro a la codificación dada si el parámetro resulta ser una cadena Unicode, o lo deja solo si es una cadena de 8 bits, asumiendo que ya está en la codificación deseada. Esto difiere del carácter de formato `es`, que asume que las cadenas de 8 bits están en la codificación ASCII por defecto de Python y las convierte a la nueva codificación especificada. (Contribuido por M.-A. Lemburg, y utilizado para el soporte de MBCS en Windows descrito en la siguiente sección)

- A different argument parsing function, `PyArg_UnpackTuple()`, has been added that’s simpler and presumably faster. Instead of specifying a format string, the caller simply gives the minimum and maximum number of arguments expected, and a set of pointers to `PyObject*` variables that will be filled in with argument values.

- Dos nuevos indicadores `METH_NOARGS` y `METH_O` están disponibles en las tablas de definición de métodos para simplificar la implementación de métodos sin argumentos o con un único argumento no tipado. Llamar a estos métodos es más eficiente que llamar a un método correspondiente que utilice `METH_VARARGS`. Además, el antiguo estilo `METH_OLDARGS` de escribir métodos en C está oficialmente en desuso.

- Se han añadido dos nuevas funciones de envoltura, `PyOS_snprintf()` y `PyOS_vsnprintf()` para proporcionar implementaciones multiplataforma para las relativamente nuevas APIs de la biblioteca C `snprintf()` y `vsnprintf()`. A diferencia de las funciones estándar `sprintf()` y `vsprintf()`, las versiones de Python comprueban los límites del búfer utilizado para protegerse de los desbordamientos del mismo. (Contribución de M.-A. Lemburg.)

- La función `_PyTuple_Resize()` ha perdido un parámetro que no se utilizaba, por lo que ahora toma 2 parámetros en lugar de 3. El tercer argumento nunca se utilizaba, y puede descartarse simplemente al portar el código de versiones anteriores a Python 2.2.

## Otros cambios y correcciones

Como es habitual, hubo un montón de otras mejoras y correcciones de errores repartidas por todo el árbol de fuentes. Una búsqueda en los registros de cambios de CVS revela que se aplicaron 527 parches y se corrigieron 683 errores entre Python 2.1 y 2.2; en 2.2.1 se aplicaron 139 parches y se corrigieron 143 errores; en 2.2.2 se aplicaron 106 parches y se corrigieron 82 errores. Es probable que estas cifras estén subestimadas.

Algunos de los cambios más notables son:

- El código del puerto MacOS para Python, mantenido por Jack Jansen, se mantiene ahora en el árbol CVS principal de Python, y se han realizado muchos cambios para soportar MacOS X.

  El cambio más significativo es la capacidad de construir Python como un marco de trabajo, que se activa proporcionando la opción `--enable-framework` al script de configuración cuando se compila Python. Según Jack Jansen, «Esto instala una instalación autónoma de Python más el «pegamento» del framework de OS X en `/Library/Frameworks/Python.framework` (o en otra ubicación de su elección). Por ahora hay poco beneficio inmediato añadido a esto (en realidad, existe la desventaja de que tienes que cambiar tu PATH para poder encontrar Python), pero es la base para crear una aplicación Python completa, portar el IDE de MacPython, posiblemente usar Python como un lenguaje de scripting estándar de OSA y mucho más.»

  La mayoría de los módulos de la caja de herramientas de MacPython, que interactúan con las APIs de MacOS como ventanas, QuickTime, scripts, etc. han sido portados a OS X, pero se han dejado comentados en `setup.py`. Las personas que quieran experimentar con estos módulos pueden descomentarlos manualmente.

- Los argumentos de palabras clave pasados a funciones incorporadas que no los aceptan ahora provocan una excepción `TypeError`, con el mensaje «function no acepta argumentos de palabras clave».

- Las referencias débiles, añadidas en Python 2.1 como un módulo de extensión, son ahora parte del núcleo porque se utilizan en la implementación de clases de nuevo estilo. Por lo tanto, la excepción `ReferenceError` se ha movido del módulo `weakref` para convertirse en una excepción incorporada.

- Un nuevo script, `Tools/scripts/cleanfuture.py` de Tim Peters, elimina automáticamente las sentencias `__future__` obsoletas del código fuente de Python.

- Se ha añadido un argumento adicional flags a la función incorporada `compile()`, por lo que el comportamiento de las sentencias `__future__` puede ahora observarse correctamente en shells simulados, como los presentados por IDLE y otros entornos de desarrollo. Esto se describe en PEP 264. (Contribución de Michael Hudson)

- La nueva licencia introducida con Python 1.6 no era compatible con la GPL. Esto se ha solucionado con algunos cambios textuales menores en la licencia 2.2, de modo que ahora es legal volver a incrustar Python dentro de un programa con licencia GPL. Tenga en cuenta que Python en sí mismo no es GPL, sino que está bajo una licencia que es esencialmente equivalente a la licencia BSD, igual que siempre. Los cambios en la licencia también se aplicaron a las versiones 2.0.1 y 2.1.1 de Python.

- Cuando se presenta un nombre de archivo Unicode en Windows, Python ahora lo convertirá en una cadena codificada en MBCS, como la que utilizan las APIs de archivos de Microsoft. Como las APIs de archivos utilizan explícitamente MBCS, la elección de Python de ASCII como codificación por defecto resulta ser una molestia. En Unix, se utiliza el juego de caracteres de la localización si `locale.nl_langinfo(CODESET)` está disponible. (El soporte de Windows fue contribuido por Mark Hammond con la ayuda de Marc-André Lemburg. El soporte para Unix fue añadido por Martin von Löwis)

- La compatibilidad con archivos de gran tamaño ya está activada en Windows. (Contribución de Tim Peters.)

- El script `Tools/scripts/ftpmirror.py` ahora analiza un archivo `.netrc`, si tiene uno. (Contribución de Mike Romberg)

- Algunas características del objeto retornado por la función `xrange()` están ahora obsoletas, y provocan advertencias cuando se accede a ellas; desaparecerán en Python 2.3. Los objetos `xrange` intentaban fingir que eran tipos de secuencia completos soportando el troceado, la multiplicación de secuencias y el operador `in`, pero estas características se utilizaban raramente y, por lo tanto, tenían errores. El método `tolist()` y los atributos `start`, `stop` y `step` también han quedado obsoletos. A nivel de C, el cuarto argumento de la función `PyRange_New()`, `repeat`, también ha quedado obsoleto.

- Hubo un montón de parches para la implementación del diccionario, sobre todo para arreglar posibles vertidos del núcleo si un diccionario contiene objetos que cambian furtivamente su valor hash, o mutan el diccionario que contienen. Durante un tiempo python-dev cayó en un suave ritmo de Michael Hudson encontrando un caso que volcaba el núcleo, Tim Peters corrigiendo el error, Michael encontrando otro caso, y así sucesivamente.

- En Windows, Python puede ahora compilarse con Borland C gracias a una serie de parches aportados por Stephen Hansen, aunque el resultado aún no es totalmente funcional. (Pero esto es un progreso…)

- Otra mejora de Windows: Wise Solutions ofreció generosamente a PythonLabs el uso de su sistema InstallerMaster 8.1. Los anteriores instaladores de PythonLabs para Windows utilizaban Wise 5.0a, que estaba empezando a mostrar su edad. (Empaquetado por Tim Peters)

- Los archivos que terminan en `.pyw` pueden importarse ahora en Windows. `.pyw` es algo exclusivo de Windows, que se utiliza para indicar que un script debe ejecutarse utilizando PYTHONW.EXE en lugar de PYTHON.EXE para evitar que aparezca una consola DOS para mostrar la salida. Este parche hace posible la importación de tales scripts, en caso de que también se puedan utilizar como módulos. (Implementado por David Bolen)

- En las plataformas en las que Python utiliza la función C `dlopen()` para cargar módulos de extensión, ahora es posible establecer las banderas utilizadas por `dlopen()` utilizando las funciones `sys.getdlopenflags()` y `sys.setdlopenflags()`. (Contribución de Bram Stolk.)

- La función incorporada `pow()` ya no admite 3 argumentos cuando se suministran números de punto flotante. `pow(x, y, z)` retorna `(x**y) % z`, pero esto nunca es útil para números de punto flotante, y el resultado final varía de forma impredecible dependiendo de la plataforma. Una llamada como `pow(2.0, 8.0, 7.0)` lanzará ahora una excepción `TypeError`.

## Agradecimientos

El autor desea agradecer a las siguientes personas sus sugerencias, correcciones y ayuda en varios borradores de este artículo: Fred Bremmer, Keith Briggs, Andrew Dalke, Fred L. Drake, Jr, Carel Fellinger, David Goodger, Mark Hammond, Stephen Hansen, Michael Hudson, Jack Jansen, Marc-André Lemburg, Martin von Löwis, Fredrik Lundh, Michael McLay, Nick Mathewson, Paul Moore, Gustavo Niemeyer, Don O’Donnell, Joonas Paalasma, Tim Peters, Jens Quade, Tom Reinhardt, Neil Schemenauer, Guido van Rossum, Greg Ward, Edward Welbourne.