Qué hay de nuevo en Python 3.1

Autor:

Raymond Hettinger

Este artículo explica las nuevas características de Python 3.1, en comparación con 3.0. Python 3.1 se lanzó el 27 de junio de 2009.

PEP 372: Diccionarios ordenados

Los diccionarios de Python normales iteran sobre los pares clave/valor en orden arbitrario. A través de los años, varios autores han escrito implementaciones alternativas que recuerdan el orden en el que se insertaron originalmente las claves. Basándose en las experiencias de esas implementaciones, una nueva clase collections.OrderedDict ha sido introducida.

La API de OrderedDict es sustancialmente la misma que la de los diccionarios normales, pero iterará sobre las claves y los valores en un orden garantizado, dependiendo de cuándo se insertó una clave por primera vez. Si una nueva entrada sobrescribe una entrada existente, la posición de inserción original se deja sin cambios. Eliminar una entrada y volver a insertarla la moverá hasta el final.

La biblioteca estándar ahora admite el uso de diccionarios ordenados en varios módulos. El módulo configparser los usa por defecto. Esto permite leer, modificar y volver a escribir los archivos de configuración en su orden original. El método _asdict() para collections.namedtuple() ahora devuelve un diccionario ordenado con los valores que aparecen en el mismo orden que los índices de tupla subyacentes. El módulo json se está construyendo con un object_pairs_hook para permitir que el decodificador construya OrderedDicts. También se agregó soporte para herramientas de terceros como PyYAML.

Ver también

PEP 372 - Diccionarios ordenados

PEP escrito por Armin Ronacher y Raymond Hettinger. Implementación escrita por Raymond Hettinger.

Dado que un diccionario ordenado recuerda su orden de inserción, se puede utilizar junto con la ordenación para crear un diccionario ordenado:

>>> # diccionario regular sin clasificar
>>> d = {'banana': 3, 'manzana':4, 'pera': 1, 'naranja': 2}

>>> # diccionario ordenado por clave
>>> OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('manzana', 4), ('banana', 3), ('naranja', 2), ('pera', 1)])

>>> # diccionario ordenado por valor
>>> OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pera', 1), ('naranja', 2), ('banana', 3), ('manzana', 4)])

>>> # diccionario ordenado por longitud de la clave cadena
>>> OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
OrderedDict([('pera', 1), ('manzana', 4), ('naranja', 2), ('banana', 3)])

Los nuevos diccionarios ordenados mantienen su orden de clasificación cuando se eliminan entradas, pero cuando se agregan nuevas claves, estas se agregan al final y no se mantiene el orden.

PEP 378: Especificador de formato para el separador de miles

La función incorporada format() y el método str.format() usan un mini-lenguaje que ahora incluye una forma simple, que no tiene en cuenta la configuración regional, de formatear un número con un separador de miles. Eso proporciona una manera de humanizar la salida de un programa, mejorando su apariencia profesional y legibilidad:

>>> formato(1234567, ',d')
'1,234,567'
>>> formato(1234567.89, ',.2f')
'1,234,567.89'
>>> formato(12345.6 + 8901234.12j, ',f')
'12,345.600000+8,901,234.120000j'
>>> formato(Decimal('1234567.89'), ',f')
'1,234,567.89'

Los tipos soportados son int, float, complex y decimal.Decimal.

Se está discutiendo cómo especificar separadores alternativos como puntos, espacios, apóstrofos o guiones bajos. Las aplicaciones que reconocen la configuración regional deben usar el especificador de formato n existente, que ya es compatible con el separadores de miles.

Ver también

PEP 378 - Especificador de formato para el separador de miles

PEP escrito por Raymond Hettinger e implementado por Eric Smith y Mark Dickinson.

Otros cambios del lenguaje

Algunos cambios pequeños en el núcleo del lenguaje Python son:

  • Los directorios y archivos zip que contienen un archivo __main__.py pueden ahora ser ejecutados directamente pasando su nombre al intérprete. El directorio/archivo zip es automáticamente insertado como la primera entrada en sys.path. (Sugerencia y parche inicial por Andy Chu; parche revisado por Phillip J. Eby y Nick Coghlan; bpo-1739468.)

  • El tipo int() ganó un método bit_length que retorna el número de bits necesarios para representar sus argumentos en binario:

    >>> n = 37
    >>> bin(37)
    '0b100101'
    >>> n.bit_length()
    6
    >>> n = 2**123-1
    >>> n.bit_length()
    123
    >>> (n+1).bit_length()
    124
    

    (Contribución de Fredrik Johansson, Victor Stinner, Raymond Hettinger y Mark Dickinson; bpo-3439.)

  • Los campos en las cadenas de formato de format() ahora pueden ser automáticamente numerados:

    >>> 'Señor {} de {}'.format('Gallahad', 'Camelot')
    'Sir Gallahad de Camelot'
    

    Anteriormente, la cadena requería campos numerados como: 'Sir {0} of {1}'.

    (Contribución de Eric Smith; bpo-5237.)

  • La función string.maketrans() ha quedado obsoleta y se ha reemplazado por nuevos métodos estáticos, bytes.maketrans() y bytearray.maketrans(). Este cambio resuelve la confusión sobre qué tipos eran compatibles con el módulo string. Ahora, str, bytes y bytearray tienen sus propios métodos maketrans y translate con tablas de traducción intermedias del tipo adecuado.

    (Contribución de Georg Brandl; bpo-5675.)

  • La sintaxis de la sentencia with ahora permite múltiples gestores de contexto en una sola declaración:

    >>> con open('mylog.txt') como archivo de entrada, open('a.out', 'w') como archivo de salida:
    ... para línea en archivo de entrada:
    ... si '<critical>' está en línea:
    ... archivo de salida.write(línea)
    

    Con la nueva sintaxis, la función contextlib.nested() ya no es necesaria y ahora está obsoleta.

    (Contribución de Georg Brandl y Mattias Brändström; appspot issue 53094.)

  • round(x, n) ahora retorna un número entero si x es un entero. Anteriormente retornaba un número flotante:

    >>> redondear(1123, -2)
    1100
    

    (Contribución de Mark Dickinson; bpo-4707.)

  • Python ahora utiliza el algoritmo de David Gay para encontrar la representación de punto flotante más corta que no cambia su valor. Esto debería ayudar a mitigar parte de la confusión que rodea a los números binarios de punto flotante.

    La importancia se aprecia fácilmente con un número como 1.1, que no tiene un equivalente exacto en coma flotante binaria. Como no hay un equivalente exacto, una expresión como float('1.1') se evalúa como el valor representable más cercano, que es 0x1.199999999999ap+0 en hexadecimal o 1.100000000000000088817841970012523233890533447265625 en decimal. Ese valor más cercano se utilizó y todavía se utiliza en cálculos de coma flotante posteriores.

    Lo nuevo es cómo se muestra el número. Anteriormente, Python utilizaba un enfoque simple. El valor de repr(1.1) se calculaba como format(1.1, '.17g'), que se evaluaba como '1.1000000000000001'. La ventaja de utilizar 17 dígitos era que dependía de las garantías IEEE-754 para asegurar que eval(repr(1.1)) regresaría exactamente a su valor original. La desventaja es que muchas personas encontraron que el resultado era confuso (confundiendo las limitaciones intrínsecas de la representación binaria de punto flotante con un problema del propio Python).

    El nuevo algoritmo para repr(1.1) es más inteligente y retorna '1.1'. Efectivamente, busca todas las representaciones de cadenas equivalentes (las que se almacenan con el mismo valor flotante subyacente) y retorna la representación más corta.

    El nuevo algoritmo tiende a emitir representaciones más limpias cuando es posible, pero esto no cambia los valores subyacentes. Por lo tanto, todavía se da el caso 1.1 + 2.2 != 3.3, aún cuando las representaciones puedan sugerir lo contrario.

    El nuevo algoritmo depende de ciertas características de la implementación de punto flotante subyacente. Si no se encuentran las características requeridas, se seguirá utilizando el algoritmo antiguo. Además, los protocolos de pickle de texto garantizan la portabilidad entre plataformas mediante el uso del algoritmo antiguo.

    (Contribución de Eric Smith y Mark Dickinson; bpo-1580)

Módulos nuevos, mejorados y obsoletos

  • Se ha añadido una clase collections.Counter para admitir el recuento conveniente de elementos únicos en una secuencia o una iteración:

    >>> Contador(['rojo', 'azul', 'rojo', 'verde', 'azul', 'azul'])
    Contador({'azul': 3, 'rojo': 2, 'verde': 1})
    

    (Contribución de Raymond Hettinger; bpo-1696199.)

  • Se ha añadido un nuevo módulo tkinter.ttk para acceder al conjunto de widgets temáticos de Tk. La idea básica de ttk es separar, dentro de lo posible, el código que implementa el comportamiento de un widget del código que implementa su apariencia.

    (Contribución de Guilherme Polo; bpo-2983.)

  • Las clases gzip.GzipFile y bz2.BZ2File ahora admiten el protocolo de gestión de contexto:

    >>> # Cerrar automáticamente el archivo después de escribir
    >>> con gzip.GzipFile(filename, "wb") como f:
    ... f.write(b"xxx")
    

    (Contribución de Antoine Pitrou.)

  • El módulo decimal ahora admite métodos para crear un objeto decimal de un float binario. La conversión es exacta pero puede ser a veces una sorpresa:

    >>> Decimal.from_float(1.1)
    Decimal('1.100000000000000088817841970012523233890533447265625')
    

    El resultado decimal largo muestra la fracción binaria real que se almacena para 1.1. La fracción tiene muchos dígitos porque 1.1 no se puede representar exactamente en binario.

    (Contribución de Raymond Hettinger y Mark Dickinson.)

  • El módulo itertools desarrolló dos nuevas funciones. La función itertools.combinations_with_replacement() es una de las cuatro para generar combinatorias que incluyen permutaciones y productos cartesianos. La función itertools.compress() imita su homónimo de APL. Además, la función existente itertools.count() tiene ahora un argumento step opcional y puede aceptar cualquier tipo de secuencia de conteo, incluyendo fractions.Fraction y decimal.Decimal:

    >>> [p+q para p,q en combinaciones_con_reemplazo('LOVE', 2)]
    ['LL', 'LO', 'LV', 'LE', 'OO', 'OV', 'OE', 'VV', 'VE', 'EE']
    
    >>> lista(comprimir(datos=rango(10), selectores=[0,0,1,1,0,1,0,1,0,0]))
    [2, 3, 5, 7]
    
    >>> c = conteo(inicio=Fracción(1,2), paso=Fracción(1,6))
    >>> [siguiente(c), siguiente(c), siguiente(c), siguiente(c)]
    [Fracción(1, 2), Fracción(2, 3), Fracción(5, 6), Fracción(1, 1)]
    

    (Contribución de Raymond Hettinger.)

  • collections.namedtuple() ahora admite un argumento de palabra clave rename que permite que los campos de nombre inválidos se conviertan automáticamente en nombres posicionados de la forma _0, _1, etc. Esto es de utilidad cuando los nombres del campo están siendo creados por una fuente externa como un encabezado CSV, una lista de campos SQL, o la entrada del usuario:

    >>> consulta = entrada()
    SELECT región, departamento, count(*) FROM main GROUPBY region, dept
    
    >>> cursor.execute(query)
    >>> query_fields = [desc[0] for desc in cursor.description]
    >>> UserQuery = namedtuple('UserQuery', query_fields, rename=True)
    >>> pprint.pprint([UserQuery(*row) para la fila en el cursor])
    [UserQuery(región='Sur', departamento='Envíos', _2=185),
    UserQuery(región='Norte', departamento='Contabilidad', _2=37),
    UserQuery(región='Oeste', departamento='Ventas', _2=419)]
    

    (Contribución de Raymond Hettinger; bpo-1818.)

  • Las funciones re.sub(), re.subn() y re.split() ahora admiten un parámetro flags.

    (Contribución de Gregory Smith.)

  • El módulo logging ahora implementa una clase simple logging.NullHandler para aplicaciones que no utilizan el registro pero llaman al código de una biblioteca que si lo hace. La configuración de un controlador nulo suprimirá las advertencias falsas como «No handlers could be found for logger foo»:

    >>> h = registro.NullHandler()
    >>> registro.getLogger("foo").addHandler(h)
    

    (Contribución de Vinay Sajip; bpo-4384).

  • El módulo runpy que admite el modificador de línea de comando -m ahora admite la ejecución de paquetes al buscar y ejecutar un submódulo __main__ cuando se proporciona un nombre de paquete.

    (Contribución de Andi Vajda; bpo-4195.)

  • El módulo pdb puede ahora acceder y mostrar el código fuente cargado a través de zipimport (o cualquier otro cargador conforme PEP 302).

    (Contribución de Alexander Belopolsky; bpo-4201.)

  • Los objetos functools.partial pueden ser ahora serializados (pickled).

(Sugerido por Antoine Pitrou y Jesse Noller. Implementado por Jack Diederich; bpo-5228.)

  • Se agrega temas de ayuda pydoc para símbolos de modo que help('@') funcione como se espera en un entorno interactivo.

    (Contribución de David Laban; bpo-4739.)

  • El módulo unittest ahora admite saltear pruebas individuales o clases de pruebas. Y admite marcar una prueba como una falla esperada, una prueba que se sabe que está rota, pero que no debe contarse como una falla en un TestResult:

    clase TestGizmo(unittest.TestCase):
    
    @unittest.skipUnless(sys.platform.startswith("win"), "requiere Windows")
    def test_gizmo_on_windows(self):
    ...
    
    @unittest.expectedFailure
    def test_gimzo_without_required_library(self):
    ...
    

    Además, se han creado pruebas de excepciones para trabajar con gestores de contexto usando la declaración with:

    def test_division_by_zero(self):
    con self.assertRaises(ZeroDivisionError):
    x / 0
    

    Además, se agregaron varios métodos de afirmación nuevos, incluidos assertSetEqual(), assertDictEqual(), assertDictContainsSubset(), assertListEqual(), assertTupleEqual(), assertSequenceEqual(), assertRaisesRegexp(), assertIsNone() y assertIsNotNone().

    (Contribución de Benjamin Peterson y Antoine Pitrou.)

  • El módulo io tiene tres nuevas constantes para el método seek(): SEEK_SET, SEEK_CUR y SEEK_END.

  • La tupla sys.version_info ahora es una tupla con nombre:

    >>> sys.version_info
    sys.version_info(mayor=3, menor=1, micro=0, nivel de versión='alfa', serial=2)
    

    (Contribución de Ross Light; bpo-4285.)

  • Los módulos nntplib y imaplib ahora admiten IPv6.

    (Contribución de Derek Morr; bpo-1655 y bpo-1664.)

  • El módulo pickle ha sido adaptado para una mejor interoperabilidad con 2.x cuando es usado con un protocolo 2 o menor. La reorganización de la biblioteca estándar cambió la referencia formal para varios objetos. Por ejemplo, __builtin__.set en Python 2 es llamado builtins.set en Python 3. Este cambio confundió los esfuerzos de compartir datos entre diferentes versiones de Python. Pero ahora cuando el protocolo 2 o menor es seleccionado, el pickler va a usar automáticamente los nombres antiguos de Python 2 tanto para carga como para volcado. Esta reasignación es activada de manera predeterminada pero puede ser desactivada con la opción fix_imports:

    >>> s = {1, 2, 3}
    >>> pickle.dumps(s, protocolo=0)
    b'c__builtin__\nestablecer\np0\n((lp1\nL1L\naL2L\naL3L\natp2\nRp3\n.'
    >>> pickle.dumps(s, protocolo=0, fix_imports=False)
    b'cbuiltins\nestablecer\np0\n((lp1\nL1L\naL2L\naL3L\natp2\nRp3\n.'
    

    Un efecto secundario, desafortunado pero inevitable, de este cambio es que los pickles del protocolo 2 producidos con Python 3.1 no serán legibles con Python 3.0. El protocolo de pickle más reciente, protocolo 3, debe utilizarse al migrar datos entre implementaciones de Python 3.x, ya que no intenta seguir siendo compatible con Python 2.x.

    (Contribución de Alexandre Vassalotti y Antoine Pitrou, bpo-6137.)

  • Se agregó un nuevo módulo, importlib. Proporciona una implementación de referencia de Python completa, portátil y pura de la instrucción import y su contraparte, la función __import__() . Representa un avance sustancial en la documentación y definición de las acciones que tienen lugar durante las importaciones.

    (Contribución de Brett Cannon.)

Optimizaciones

Se han agregado importantes mejoras de rendimiento:

  • La nueva biblioteca I/O (definida en PEP 3116) estaba escrita mayormente en Python y rápidamente demostró ser un cuello de botella problemático en Python 3.0. En Python 3.1, la biblioteca I/O ha sido reescrita enteramente en C y es de 2 a 20 veces más rápida dependiendo en la tarea a manejar. La versión puramente en Python está aún disponible para fines experimentales a través del módulo _pyio.

    (Contribución de Amaury Forgeot d’Arc y Antoine Pitrou.)

  • Se ha añadido una heurística para que el recolector de basura no realice el seguimiento de tuplas y diccionarios que contengan solo objetos no rastreables. Esto puede reducir el tamaño de las colecciones y, por lo tanto, la sobrecarga de recolección de elementos no utilizados en programas de larga ejecución, en función de su uso particular de los tipos de datos.

    (Contribución de Antoine Pitrou, bpo-4688.)

  • Habilitando una opción de configuración llamada --with-computed-gotos en compiladores que la admiten (en particular: gcc, SunPro, icc), el ciclo de evaluación del bytecode se compila con un nuevo mecanismo de despacho que proporciona aceleraciones de hasta un 20% , dependiendo del sistema, del compilador y del punto de referencia.

    (Contribución de Antoine Pitrou junto con varios otros participantes, bpo-4753).

  • La decodificación de UTF-8, UTF-16 y LATIN-1 es ahora de dos a cuatro veces más rápida.

    (Contribución de Antoine Pitrou y Amaury Forgeot d’Arc, bpo-4868.)

  • El módulo json ahora tiene una extensión C para mejorar sustancialmente su rendimiento. Además, se modificó la API para que json funcione solo con str, no con bytes. Ese cambio hace que el módulo coincida estrechamente con el JSON specification que se define en términos de Unicode.

    (Contribución de Bob Ippolito y convertido a Py3.1 por Antoine Pitrou y Benjamin Peterson; bpo-4136.)

  • La deserialización (unpickling) ahora interna los nombres de los atributos de los objetos serializados (pickled). Esto ahorra memoria y permite que los pickles sean más pequeños.

    (Contribución de Jake McGuire y Antoine Pitrou; bpo-5084.)

IDLE

  • El menú de formato del IDLE ahora proporciona una opción para eliminar los espacios en blanco finales de un archivo de código fuente.

    (Contribución de Roger D. Serwy; bpo-5150.)

Cambios en la compilación y la API de C

Los cambios en el proceso de compilación de Python y en la API C incluyen:

  • Los enteros ahora se almacenan internamente en base 2**15 o en base 2**30, la base se determina en el momento de la construcción. Anteriormente, siempre se almacenaban en base 2**15. El uso de base 2**30 ofrece mejoras significativas en el rendimiento en máquinas de 64 bits, pero los resultados de las pruebas comparativas en máquinas de 32 bits han sido diversos. Por lo tanto, el valor predeterminado es usar base 2**30 en máquinas de 64 bits y base 2**15 en máquinas de 32 bits; en Unix, hay una nueva opción de configuración --enable-big-digits que puede ser usada para sobre escribir este valor predeterminado.

    Aparte de las mejoras de rendimiento, este cambio debería ser invisible para los usuarios finales, con una excepción: para fines de prueba y depuración hay un nuevo sys.int_info que proporciona información sobre el formato interno, dando el número de bits por dígito y el tamaño en bytes del tipo C utilizado para almacenar cada dígito:

    >>> import sys
    >>> sys.int_info
    sys.int_info(bits_per_digit=30, sizeof_digit=4)
    

    (Contribución de Mark Dickinson; bpo-4258.)

  • La función PyLong_AsUnsignedLongLong() es ahora capaz de manejar un pylong negativo lanzando una excepción OverflowError en lugar de TypeError.

    (Contribución de Mark Dickinson y Lisandro Dalcrin; bpo-5175.)

  • PyNumber_Int() está ahora obsoleto. Utilice PyNumber_Long() en su lugar.

    (Contribución de Mark Dickinson; bpo-4910.)

  • Se agregó una nueva función PyOS_string_to_double() para reemplazar las funciones obsoletas PyOS_ascii_strtod() y PyOS_ascii_atof().

    (Contribución de Mark Dickinson; bpo-5914.)

  • Se agregó PyCapsule como reemplazo de la API PyCObject. La principal diferencia es que el nuevo tipo tiene una interfaz bien definida para pasar información de seguridad de tipado y una firma menos complicada para llamar a un destructor. El tipo anterior tenía una API problemática y ahora está obsoleto.

    (Contribución de Larry Hastings; bpo-5630.)

Portando a Python 3.1

Esta sección enumera los cambios descritos anteriormente y otras correcciones de errores que pueden requerir cambios en su código:

  • Las nuevas representaciones de cadenas de punto flotante pueden romper las pruebas de documentación existentes. Por ejemplo:

    def e():
        '''Compute the base of natural logarithms.
    
        >>> e()
        2.7182818284590451
    
        '''
        return sum(1/math.factorial(x) for x in reversed(range(30)))
    
    doctest.testmod()
    
    **********************************************************************
    Failed example:
        e()
    Expected:
        2.7182818284590451
    Got:
        2.718281828459045
    **********************************************************************
    
  • La reasignación automática de nombres en el módulo pickle para el protocolo 2 o inferior puede hacer que los pickles de Python 3.1 sean ilegibles en Python 3.0. Una solución es usar el protocolo 3. Otra solución es establecer la opción fix_imports en False. Consulte la discusión anterior para obtener más detalles.