El pasado día once de Junio del 2011 fue liberada la versión 2.7.2 de Python. Esta nueva versión incluye muchas de las características que fueron primeramente liberadas en la versión 3.1 de Python. Entre las novedades en Python 2.7.2 se incluye:
Un tipo de diccionario ordenado
Nuevas características en unittest como test skipping, nuevos métodos assert y test descovery
Un módulo io mucho más rápido
Numeración automática de los campos del método str.format()
Mejoras a repr(float) portadas desde la versión 3.x
Soporte de tiles en Tkinter
Un backport del objeto memoryview desde la versión 3.x
Un conjunto de literales
Compresión de conjuntos y diccionarios
Vistas de diccionario
Nueva sintaxis para declaraciones anidadas
El módulo sysconfig
¿Y eso que significa?
Todos estos cambios están muy bien, y seguramente habrás tenido la oportunidad de leerlos en blogs que se hayan hecho eco de la release de la nueva versión, pero en Genbeta Dev vamos a profundizar un poco intentando explicar el significado de cada uno de ellos.
Un tipo de diccionario ordenado
Desde siempre, los diccionarios en Python, han iterado sobre pares de clave/valor de manera arbitraria. Eso me ha dado muchos quebraderos de cabeza en más de una ocasión, sobre todo con el desarrollo de Goliat. Normalmente, lo que hacíamos los desarrolladores de Python era crear nuestra propia implementación del objeto dict para que se recordara el orden en que las claves habían sido insertadas. Por fin Python 2.7.2 incluye un nuevo objeto en el módulo collections llamado OrderedDict que hace precisamente eso.
La API de OrderedDict ofrece la misma interfaz que un diccionario normal pero itera sobre claves y valores garantizando un orden preestablecido dependiendo de cuando fue una clave insertada en el diccionario:
>>> from collections import OrderedDict
>>> d = OrderedDict([('pera', 1), ('manzana', 2), ('platano', 3)])
>>> d.items()
ItemsView(OrderedDict([('pera', 1), ('manzana', 2), ('platano', 3)]))
>>>
Si se sobreescribe uno de los elementos del diccionario, mantiene su posición original:
>>> d['manzana'] = 6
>>> d.items()
ItemsView(OrderedDict([('pera', 1), ('manzana', 6), ('platano', 3)]))
>>>
Eliminar un elemento y reinsertarlo después, lo moverá al final:
>>> del d['manzana']
>>> d['manzana'] = 2
>>> d.items()
ItemsView(OrderedDict([('pera', 1), ('platano', 3), ('manzana', 2)]))
>>>
El método popitem tiene un argumento last
opcional que por defecto es True. Si el argumento es True el elemento más reciente insertado será devuelto y eliminado del diccionario, si por el contrario el argumento es False la clave más vieja será devuelta y eliminada:
>>> del d
>>> d = OrderedDict([('pera', 1), ('manzana', 2), ('platano', 3)])
>>> d.popitem()
('platano', 3)
>>> d = OrderedDict([('pera', 1), ('manzana', 2), ('platano', 3)])
>>> d.popitem(last=False)
('pera', 1)
>>>
Cuando se comparan dos OrderedDict se comprueba que tanto las claves y los valores como el orden sean iguales:
>>> d1 = OrderedDict([('pera', 1), ('manzana', 2), ('platano', 3)])
>>> d2 = OrderedDict([('planato', 3), ('manzana', 2), ('pera', 1)])
>>> d1 == d2
False
>>> d2 = OrderedDict([('pera', 1), ('manzana', 2), ('platano', 3)])
>>> d1 == d2
True
>>>
Si se compara un diccionario normal con un OrderedDict tan solo se comprobará que las claves y valores sean iguales y se ignora el oden:
>>> d1 = OrderedDict([('pera', 1), ('manzana', 2), ('platano', 3)])
>>> d2 = {'platano' : 3, 'pera' : 1, 'manzana' : 2}
>>> d1 == d2
True
>>>
Nuevas características en unittest
El módulo unittest ha sido extremadamente potenciado añadiéndole nuevas características. Si se usa desde la línea de comandos, el módulo puede descubrir (test discover) tests y provee de un medio sencillo de ejecutar tests en un conjunto de paquetes en directorios:
genbeta@dev ~ $ python -m unittest discover -s test
El comando anterior buscará en el directorio test/ cualquier test importable de un conjunto de archivos con nombre test*.py. Más opciones han sido añadidas a la línea de comandos, para tener una información más detallada de las mismas puedes dirigirte a la página del manual de la línea de comandos de unittest.
Los casos de test (TestCase) pueden elevar la excepción SkipTest para saltar un test. Los mensajes de error de las funciones relacionadas con asserts (assertEqual, assertTrue, assertFalse) ahora proveen de más información cuando fallan.
El método assertRaises ahora devuelve un manejador de contexto cuando es invocado sin un objeto invocable para ejecutar. Ahora podemos escribir cosas como esta:
with self.assertRaises(KeyError) :
{}['fake']
Fixtures para setup y teardown están ahora soportadas a nivel de clase y módulo. Los módulos pueden implementar las funciones setUpModule y tearDownModule. Las clases pueden implementar los métodos setUpClass y tearDownClass que deben de ser definidos como métodos de clase utilizando @classmethod
o similares.
Se han añadido los métodos addCleanup y doCleanups. addCleanup te permite invocar funciones de limpieza que se ejecutarán de manera incondicional. Esto permite asignar y liberar recursos de manera mucho más simple durante los tests.
También se han añadido una cantidad nada despreciable de nuevos métodos desarrollados por los ingenieros de Google. unittest.main ahora acepta un argumento exit
opcional. Si es False main()
no invoca a sys.exit()
, permitiendo que main() sea utilizado de forma interactiva desde el intérprete.
TestResult tiene dos métodos nuevos llamados startTestRun y stopTestRun que son invocados inmediatamente antes y después de que de un test se ejecute.
Mejoras de rendimiento al módulo io
El módulo io ha sido completamente reescrito en C para mejorar su rendimiento.
Numeración automática de los campos del método str.format()
Un mecanismo de agrupación por comas ha sido añadido al método str.format(). Cuando se formatea un número de coma flotante, simplemente se añade una coma entre el ancho y la precisión:
>>> '{:20,.4f}'.format(24587452365445487542198.0)
'24,587,452,365,445,487,656,960.0000'
>>>
Mejoras a repr float portadas desde la versión 3.x
La conversión entre números de coma flotante y cadenas ahora se redondean de forma correcta en la mayoría de las plataformas. La representación de un número de coma flotante mediante repr() ahora devuelve un resultado basado en la cadena con el decimal más corto que garantice el redondeo correcto del número de coma flotante al convertirlo de cadena otra vez a float:
>>> n = 4572121446565654454545452154
>>> float(n)
4.5721214465656543e+27
>>> repr(float(n))
'4.5721214465656543e+27'
>>> float(repr(float(n))) - float(n)
0.0
>>>
Backport del objeto memoryview
El objeto memoryview provee de una vista del contenido en memoria de otro objeto que coincida con la interfaz de tipo bytes:
>>> import string
>>> m = memoryview(string.letters)
>>> m
>>> len(m)
52
>>> m[0], m[25], m[26]
('a', 'z', 'A')
>>> m2 = m[0:26]
>>> m2
Un conjunto de literales
La sintaxis para fijar literales ha sido portada desde la versión 3.x de Python. Se usan llaves para rodear el contenido de un conjunto mutable. Las literales de conjunto se distinguen de los diccionarios por que no contienen dos puntos y un valor detrás de una clave. {}
representa un diccionario vació set()
representa un conjunto vacío:
>>> {1, 2, 3, 4, 5}
{1, 2, 3, 4, 5}
>>> set()
set()
>>> {}
{}
>>>
Compresión de conjuntos y diccionarios
Se ha portado la comprensión de diccionarios y conjuntos desde Python 3.x para poder usar la sintaxis literal:
>>> {x: x*x for x in range(10)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
>>> {('a'*x) for x in range(10)}
{'', 'a', 'aa', 'aaa', 'aaaaaaaaa', 'aaaaaaaa', 'aaaa', 'aaaaa', 'aaaaaaa', 'aaaaaa'}
>>>
Vistas de diccionario
Se han añadido los métodos viewkeys, viewvalues, y viewitems que emulan el comportamiento de los métodos keys, values y items en Python 3.x. No se ha portado con los mismos nombres por que obviamente se rompería todo el código escrito con Python 2.x anterior.
>>> d1 = dict((i*10, chr(65+i)) for i in range(26))
>>> d2 = dict((i**.5, i) for i in range(1000))
>>> d1.viewkeys() & d2.viewkeys()
set([0.0, 10.0, 20.0, 30.0])
>>> d1.viewkeys() | range(0, 30)
set([0, 1, 130, 3, 4, 5, 6, ..., 120, 250])
El módulo sysconfig
El módulo sysconfig ha sido extraido del paquete Distutils y se ha convertido en un nuevo módulo a parte. Provee de información sobre el proceso de construcción de Python: modificadores del compilador, rutas de instalación, el nombre de la plataforma, y si Python se está ejecutando desde el directorio de las fuentes. Algunas de las funciones del módulo son:
get_config_var() devuelve variables del Makefile de Python y del archivo
pyconfig.h
get_config_vars() devuelve un diccionario que contiene todas las variables de configuración
getpath() devuelve el path configurado para un determinado tipo de módulo: la librería estándar, módulos del site-packages, módulo de la plataforma, etc
is_python_build() devuelve true si estas ejecutando un binario del árbol de origen de Python y falso de cualquier otra manera
Como puedes apreciar, esta última release viene cargadita de novedades. Según el PEP 373 la versión definitiva de Python 2.7 (y la última de la serie Python 2.x) será liberada en Julio, y lo cierto es que creo que están en el tiempo previsto.
Más Información | Documentación de Python