Novedades de Python 3.1

Publicado por Euribates el 9:45 AM | 1 comentarios | ,

Novedades de Python 3.1

Traducción del artículo original de Raymond Hettinger - What's New in Python 3.1. En este artículo se repasan las principales diferencias de Python 3.1 con respecto a la versión 3.0.

PEP 372: Diccionarios con orden

Los diccionarios normales en Python se pueden iterar, en forma de secuencia de parejas clave/valor, pero no se sigue un orden establecido. Durante mucho tiempo, diversos autores han propuesto implementaciones alternativas, que recordaban el orden en que se insertaron inicialmente las claves. Basándose en lo aprendido con estas implementaciones, se ha creado una nueva clase collections.OrderedDict.

La API de OrderedDict es básicamente la misma que la de un diccionario normal, pero se garantiza que las iteraciones sobre claves y valores se producirán en el mismo orden en que las claves se insertaron originalmente. Si se reescribe un valor asociado con una clave ya existente, se mantiene la posición de la inserción original. Para forzar a que una entrada ya almacenada se traslade al final, debemos borrarla e insertarla de nuevo.

La librería estándar utiliza desde ahora esta nueva clase en varios módulos. El módulo configparser, por ejemplo, los usa ahora por defecto, de forma que al leer, modificar y volver a escribir un fichero de configuración, este mantiene su orden inicial. El método _asdict() de la clase collections.namedtuple() también devuelve un diccionario ordenado, de forma que los valores siguen el mismo orden de la tupla original. El módulo json incluye la posibilidad de usar una función callback* (usando el parámetro opcional object_pairs_hook) que permite que los resultados devueltos por el decodificador sean diccionarios ordenados. También se han incluido en herramientas de terceras partes como PyYAML.

Note

PEP 372 - Diccionarios ordenados

PEP 378: Especificador de formato para millares

La función format() y el método str.format() utilizan un mini-lenguaje para indicar formatos, y dicho lenguaje incluye ahora la posibilidad de indicar, de forma simple e independiente de la configuración local del sistema, que se debe usar un carácter separador para indicar los millares. De esta forma se puede obtener un número más fácil de leer por los humanos, mejorando así tanto la apariencia como la legibilidad:

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

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

Por el momento aún se están discutiendo la mejor manera de poder indicar caracteres separadores alternativos, como puntos, espacios, apóstrofes o subrayados. Las aplicaciones que prefieran atenerse a lo indicado en la configuración local deben usar el indicador de formato ya existente, n, que ya incorpora algo de soporte para separadores de millares.

Note

PEP 378 - Especificador de formato para millares

Otros cambios en el lenguaje

Algunos cambios menores llevados a cabo en el núcleo del lenguaje son:

  • El tipo int() adquiere un nuevo método, bit_length, que devuelve el número de bits que hacen falta para representar su argumento 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; issue 3439.)

  • Los campos en las cadenas de formato de format() se numeran automáticamente:

    >>> 'Sir {} of {}'.format('Gallahad', 'Camelot')
    'Sir Gallahad of Camelot'
    

    Antes, la cadena de texto debía incluir campos numerados como en:

    'Sir {0} of {1}'
    

    (Contribución de Eric Smith; issue 5237).

  • La función string.maketrans() queda desfasada, y es reemplazada por dos nuevos métodos estáticos, bytes.maketrans() y bytearray.maketrans(). De esta forma se soluciona la confusión que existía acerca de los tipos de datos soportados por el módulo string. Ahora, cada tipo de datos: str, bytes y bytearray dispone de sus propios métodos maketrans y translate, que usan tablas de traducción apropiadas para cada tipo.

    (Contribución de Georg Brandl; issue 5675).

  • La sintaxis de la sentencia with permite ahora usar varios gestores de contexto en una única sentencia:

    >>> with open('mylog.txt') as infile, open('a.out', 'w') as outfile:
    ...     for line in infile:
    ...         if '<critical>' in line:
    ...             outfile.write(line)
    

    Con esta nueva sintaxis, la función contextlib.nested() ya no hace falta, y queda desfasada.

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

  • round(x, n) ahora devuelve un entero si x es entero; antes devolvía un número en coma flotante:

    >>> round(1123, -2)
    1100
    

    (Contribución de Mark Dickinson; issue 4707).

  • Python utiliza ahora el algoritmo de David Gay, que encuentra la representación de un número en coma flotante más corta posible, de manera tal que no cambie su valor. Esto debería ayudar a aclarar algunas confusiones habituales que ocurren con la representación de números de coma flotante en binario.

    Esto se ve claramente con números como 1.1, que no tienen representación exacta en binario con coma flotante. Dado que no hay una representación exacta, una expresión como float(1.1) se evalúa al número más cercano que si tiene representación exacta, que en este caso es 0x1.199999999999ap+0 en hexadecimal o 1.100000000000000088817841970012523233890533447265625 en decimal. Este valor más cercano es que el se usará en subsiguientes operaciones de coma flotante.

    La novedad consiste en como se muestra ahora el valor. Antes, la aproximación de Python era bastante sencilla. El valor de repr(1.1) se calculaba como format(1.1, '.17g'), lo que resultaba en '1.1000000000000001'. La ventaja de usar esos diecisiete dígitos fraccionarios era garantizar que, siguiendo la norma IEEE-754, la expresión eval(repr(1.1)) devolvería un valor redondeado que sería exactamente igual al original. La desventaja era que mucha gente no entendía este comportamiento (confundiendo las limitaciones intrínsecas de la representación en binario con coma flotante con un problema propio de Python).

    El algoritmo usado ahora para repr(1.1) es más inteligente, y devuelve '1.1'. Internamente, lo que hace es buscar todas las cadenas de texto equivalentes (es decir, las que se almacenarían con el mismo valor en coma flotante), y devuelve la más corta.

    El nuevo algoritmo produce representaciones más claras, cuando es posible, pero no cambia para nada el almacenamiento interno del valor, por lo que sigue pasando que 1.1 + 2.2 != 3.3, aun cuando la representación puede sugerir lo contrario.

    El nuevo algoritmo requiere de ciertas capacidades en la implementación subyacente de números en coma flotante, por lo que se sigue utilizando el algoritmo antiguo si dichas características no están presentes en la plataforma. Además, el módulo pickle, para garantizar que se mantenga la portabilidad entre plataformas diferentes, también sigue usando el algoritmo antiguo.

    (Contribución de Eric Smith y Mark Dickinson; issue 1580).

Módulos nuevos, mejorados o desfasados

  • Añadida una nueva clase, collections.Counter, para facilitar el recuento de elementos únicos dentro de una secuencia o iterable:

    >>> Counter(['red', 'blue', 'red', 'green', 'blue', 'blue'])
    Counter({'blue': 3, 'red': 2, 'green': 1})
    

    (Contribución de Raymond Hettinger; issue 1696199).

  • Se ha añadido un nuevo módulo, tkinter.ttk, que permite el acceso al conjunto de controles con temas de Tk. La idea es separar, en la medida de lo posible, el código de implementación del comportamiento de los controles del código que implementa su apariencia.

    (Contribución de Guilherme Polo; issue 2983.)

  • Las clases gzip.GzipFile y bz2.BZ2File ahora soportan en protocolo de gestión de contextos:

    >>> # Automatically close file after writing
    >>> with gzip.GzipFile(filename, "wb") as f:
    ...     f.write(b"xxx")
    

    (Contribución de Antoine Pitrou.)

  • El módulo decimal incorpora un nuevo método que permite crear un número decimal a partir de un float binario. La conversión es exacta, pero puede ser, en ocasiones, sorprendente:

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

    Este numero decimal tan largo que aparece como resultado es el número fraccionario binario que se almacena para el valor 1.1. El gran número de dígitos se debe a que un valor como 1.1 no puede representarse de forma exacta en binario.

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

  • El módulo itertools gana dos nuevas funciones. La función itertools.combinations_with_replacement() permite nuevos cálculos combinatorios, incluyendo permutaciones y productos cartesianos. La función itertools.compress() imita en nombre y comportamiento a su análoga en APL. Además, la función itertools.count() cuenta ahora con un parámetro opcional, step, y puede aceptar cualquier secuencia contable, incluyendo fractions.Fraction y decimal.Decimal:

    >>> [p+q for p,q in combinations_with_replacement('LOVE', 2)]
    ['LL', 'LO', 'LV', 'LE', 'OO', 'OV', 'OE', 'VV', 'VE', 'EE']
    
    >>> list(compress(data=range(10), selectors=[0,0,1,1,0,1,0,1,0,0]))
    [2, 3, 5, 7]
    
    >>> c = count(start=Fraction(1,2), step=Fraction(1,6))
    >>> [next(c), next(c), next(c), next(c)]
    [Fraction(1, 2), Fraction(2, 3), Fraction(5, 6), Fraction(1, 1)]
    

    (Contribución de Raymond Hettinger).

  • collections.namedtuple() soporta ahora un parámetro opcional, rename, que permite que campos con nombres no válidos se conviertan automáticamente en nombres basados en la posición, como _0, _1, etc. Este opción resulta ser muy útil para aquellos casos en que los nombres son generados a partir de una fuente externa, como una cabecera de un fichero CVS, una lista de campos de una consulta SQL, o una entrada del usuario:

    >>> query = input()
    SELECT region, dept, 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) for row in cursor])
    [UserQuery(region='South', dept='Shipping', _2=185),
     UserQuery(region='North', dept='Accounting', _2=37),
     UserQuery(region='West', dept='Sales', _2=419)]
    

    (Contribución de Raymond Hettinger; issue 1818).

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

    (Contribución de Gregory Smith.)

  • El módulo logging implementa una nueva clase, logging.NullHandler, para aquellas aplicaciones que no usan logging, pero llaman a librerías que si lo hacen. El uso de un manejador nulo como este permite evitar los mensajes de aviso espurios como "No handlers could be found for logger foo":

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

    (Contribución de Vinay Sajip; issue 4384).

  • El módulo runpy acepta ahora un nuevo parámetro de línea de comandos, -m, que permite la ejecución de un paquete mediante la búsqueda y ejecución de un submódulo __main__.

    (Contribución de Andi Vajda; issue 4195).

  • El módulo pdb puede ahora acceder y mostrar código fuente cargado con el módulo zipimport (o cualquier otro cargador de código que sea conforme al PEP 302).

    (Contribución de Alexander Belopolsky; issue 4201).

  • Los objetos de tipo functools.partial pueden ser ahora almacenados con pickled.

    (Sugerencia de Antoine Pitrou y Jesse Noller. Implementado por Jack Diedrich; issue 5228).

  • Se ha añadido documentación pydoc a los símbolos, de forma que help('&#64;') funciona tal y como se esperaría en un entorno interactivo.

    (Contribución de David Laban; issue 4739).

  • El módulo unittest ahora permite desactivar pruebas individuales o clases de pruebas, y también permite marcar una prueba como un fallo esperado, es decir, podemos hacer pruebas y marcarlas sabiendo que están mal, pero que no deben ser considerados como fallos en los resultados:

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

    Además, las pruebas que comprueban que se eleven excepciones actúan como gestores de contexto, por lo que pueden ser usadas con sentencias with:

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

    Se han incluido varios métodos de comprobación nuevos, como assertSetEqual(), assertDictEqual(), assertDictContainsSubset(), assertListEqual(), assertTupleEqual(), assertSequenceEqual(), assertRaisesRegexp(), assertIsNone(), y assertIsNotNone().

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

  • El módulo io incluye tres constantes nuevas para ser usadas con seek(): SEEK_SET, SEEK_CUR, y SEEK_END.

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

    >>> sys.version_info
    sys.version_info(major=3, minor=1, micro=0, releaselevel='alpha', serial=2)
    

    (Contribución de Ross Light; issue 4285.)

  • Los módulos nntplib y imaplib soportan ya IPv6.

    (Contribución de Derek Morr; issue 1655 e issue 1664.)

  • El módulo pickle ha sido adaptado para mejorar su interoperabilidad con Python 2.X, cuando se usa el protocolo 2 o inferior. La reorganización de la librería estándar cambió la referencia formal para muchos objetos. Por ejemplo, __builtin__.set se llama builtins.set en Python 3. Estos cambios dificultaban los intentos de compartir datos entre diferentes versiones de Python. Pero ahora, cuando se selecciona un protocolo de nivel 2 o inferior, pickler usará automáticamente los nombres antiguos de Python 2, tanto para la carga como para el almacenamiento. Este mapeo está activo por defecto, pero puede ser desactivado con la opción fix_imports:

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

    Un efecto desafortunado, pero inevitable, de estos cambios es que los datos convertidos por pickle con protocolo 2 y Python 3.1 no serán legibles en Python 3.0. Debería usarse siempre el protocolo más reciente de pickle, el 3, para los datos que se estén migrando entre implementaciones Python 3.X, ya que este protocolo no intenta mantener la compatibilidad con Python 2.X.

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

  • Se ha añadido un nuevo módulo, importlib, que es una implementación de referencia de la sentencia import (y de su contrapartida, la función __import__()) completa, escrita en Python puro y, por tanto, portable. Esto es un avance considerable en la documentación y definición de las distintas acciones que ocurren durante una importación.

    (Contribución de Brett Cannon.)

Optimizaciones

Se han incorporado algunas mejoras importantes en rendimiento.

  • La nueva librería de Entrada/Salida (Tal y como se define en el PEP 3116) estaba escrita en su mayoría en Python, y ha demostrado rápidamente ser un cuello de botella en Python 3.0. En Python 3.1, esta librería ha sido reescrita totalmente en C, y es entre 2 y 20 veces más rápida, dependiendo de la tarea que se trate. La versión escrita en Python puro sigue disponible, para el que quiera experimentar con ella, mediante el módulo _pyio.

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

  • Se ha añadido cierta heurística al recolector de basura, de forma que este trata como no trazables (untrackable) las tuplas o diccionarios que contengan sólo objetos no trazables. De esta forma se reduce el tamaño de las colecciones y, por tanto, la sobrecarga del recolector de basura para programas que están funcionando durante mucho tiempo, dependiendo del uso que hagan de los tipos de datos.

    (Contribución de Antoine Pitrou, issue 4688.)

  • Existe una nueva opción de configuración llamada --with-computed-gotos, para los compiladores que lo soporten (Especialmente gcc, SunPro e icc). Con esta opción activa, el bucle de evaluación de bytecode se compila con un nuevo mecanismo de expedición que acelera la velocidad de ejecución hasta un 20%, dependiendo del sistema, del compilador y del test de velocidad.

    (Contribución de Antoine Pitrou junto con otros participantes, issue 4753).

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

    (Contribución de Antoine Pitrou y Amaury Forgeot d'Arc, issue 4868).

  • El módulo json tiene ahora una extensión escrita en C desarrollada esencialmente para mejorar su rendimiento. Además, la API ha sido modificada para que json solo funcione con tipos str, y no con bytes. Este cambio acerca el comportamiento del módulo a la especificación JSON, que está definida en términos de Unicode.

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

  • El desempaquetado con Unpickling mantiene internamente los atributos de nombre de los objetos almacenados. Esto ahorra memoria y permite que los datos empaquetados sean más pequeños.

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

IDLE

  • El menú de formato de IDLE proporciona una nueva opción que permite eliminar los espacios en blanco superfluos al final de cada línea del código fuente.

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

Build y C API Changes

Los cambios en el proceso de compilación de Python, así como de la API en C, incluyen los siguientes:

  • Los números enteros se almacenan internamente o bien en base 2^15 o en base 2^30, determinándose la base en tiempo de compilación. Anteriormente, siempre se almacenaban en base 2^15. Usando la base 2^30 se consigue un mejor rendimiento en máquinas de 64 bits, pero los resultados con máquinas de 32 bits no eran consistentes. Por esto, el comportamiento asumido 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 se puede usar para modificar este comportamiento.

    Aparte de las mejoras en el rendimiento, esta cambio debería ser invisible para el usuario final, con una excepción: Para propósitos de prueba y de depuración, hay un valor, sys.int_info, que proporciona información acerca del formato interno; devuelve el número de bits por dígito y el tamaño en bytes del tipo de datos en C que se usa 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; issue 4258).

  • La función PyLong_AsUnsignedLongLong() ahora trata el caso de números pylong negativos, elevando una excepción de tipo OverflowError en vez de TypeError.

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

  • PyNumber_Int Queda desfasado. Se debe reemplazar por PyNumber_Long.

    (Contribución de Mark Dickinson; issue 4910).

  • Añadida una nueva función PyOS_string_to_double que reemplaza a las ya obsoletas PyOS_ascii_strtod y PyOS_ascii_atof.

    (Contribución de Mark Dickinson; issue 5914).

  • Añadido PyCapsule como reemplazo de la API PyCObject. La diferencia principal es que el nuevo tipo tiene una interfaz bien definida para pasar información de seguridad de tipos, y una signatura más sencilla en las llamadas al destructor. El tipo antiguo tenía una API problemática y queda desfasada.

    (Contribución de Larry Hastings; issue 5630).

Migración a Python 3.1

Esta sección lista, de los cambios y correcciones descritos con anterioridad, aquellos que puedan implicar cambios en tu código.

  • La nueva representación de números en coma flotante podría romper el código de baterías de pruebas (unittest) o documentación en el propio código (doctests). 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
    **********************************************************************
    
  • El mapeo automático de nombres en el módulo pickle para el protocolo 2 e inferiores puede hacer que los datos empaquetados en Python 3.1 no sean legibles para Python 3.0. Una solución es usar el protocolo 3. Otra solución sería ajustar a False la nueva opción fix_imports. Véanse los detalles en la discusión anterior sobre este tema.

Comment

THERE ARE 1 COMMENT FOR THIS POST

  1. El Candi On 11:29 PM

    Tengo una duda:
    un nuevo método, bit_length, que devuelve el número de bits que hacen falta para representar su argumento en binario:

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

    Cuando lo hice con manzanas me daba 8 y ahora me sale a 3 euros y medio?
    ¿Es grave doctor?

    Candilín
    dejalotodo@gmail.com

    ¡Pónganse a trabajar, hombre!