Python 3.9, ¿qué hay de nuevo?

Ya ha salido la versión de CPython 3.9 (casi a la vez que la versión 3.9 de Brython). Muchas cositas nuevas. Algunas te pueden afectar a la hora de programar y otras sucederán entre bambalinas y afectará en otros ámbitos como, por ejemplo, el rendimiento, la sostenibilidad y facilidad de mantenimiento del código,…

Algunos aspectos generales

El ciclo de lanzamientos ahora será cada 12 meses en lugar de cada 18 meses como venía siendo lo habitual en los últimos tiempos. Puedes ver el PEP-0602 para conocer más detalles. En base a esto, la versión 3.10.0 de CPython estará lista para octubre de 2021.

Se han incluido un montón de nuevas optimizaciones. En la tabla que ves en este enlace se puede ver cómo ha ido mejorando el rendimiento desde la versión 3.4 a la 3.9. En realidad, en esos benchmarks concretos, entre la versión 3.8 y la versión 3.9, se ve un empeoramiento en prácticamente todos ellos. Dicho esto, los tiempos siguen siendo mejores tiempos que en la versión 3.7. A pesar de lo dicho anteriormente puede que tu código corra más rápido que en la versión 3.8 gracias a que varios builtins (range, tuple, set, frozenset, list, dict) han sido acelerados mediante el uso del protocolo vectorcall (ver PEP-0590).

Se ha cambiado el analizador sintáctico (parser) pasando del anterior, LL(1), a un nuevo analizador sintáctico de tipo PEG (parsing expression grammar, ¿gramática de análisis sintáctico de expresiones?). Puedes leer más sobre ello en el PEP-0617. Básicamente, este cambio hará más mantenible el código de CPython al no necesitar hacer (¿tantas?) ñapas para solucionar las limitaciones de LL(1).

Nuevos operadores para diccionarios

En el PEP-584 se han definido nuevos operadores para unir diccionarios. ¿Cómo funciona?

Hasta ahora, si querías unir diccionarios podías usar la sintaxis:

Lo anterior no es especialmente bello. Para poder hacer uniones o actualizaciones de diccionarios se han introducido los operadores | y |=, respectivamente (¿es la mejor sintaxis?, supongo que nos acostumbraremos a ella enseguida). Un ejemplo de uso sería el siguiente:

Si lo que queremos es actualizar el primer diccionario con el segundo podemos hacer:

¿Qué pasa si una clave se repite en ambos diccionarios? Se usará la del segundo que es el último valor válido (en el caso de la unión) o el valor usado para actualizar el primer diccionario (en el caso de la actualización):

Una cosa que podemos hacer con |= que no podemos hacer con | es que podemos usar iterables para actualizar un diccionario:

Uso de tipos genéricos para anotación de tipos

Hasta ahora, incluso para definir algunos tipos básicos, había que importar cosas del módulo typing:

Lo anterior te obligaba a importar un módulo para poder añadir tipos. Eso introducía varias cosas negativas como mayor tiempo de ejecución (por importar un módulo), mezcla de tipos genéricos como int con tipos obtenidos de typing,…

Desde python 3.9, gracias al PEP-0585, puedes hacer lo siguiente:

Nuevos métodos para cadenas

Que levante la mano el que no ha hecho lo siguiente para obtener el nombre de un fichero sin su extensión:

Ahora, gracias a la introducción de dos nuevos métodos, podemos trabajar de forma más sencilla con prefijos y sufijos. Los nuevos métodos se llaman removeprefix y removesuffix y se detallan en el PEP-0616.

Imaginemos que tenemos 100 ficheros que se llaman kkk_YYYYMMDD_data.txt en los que van cambiando los valores de YYYYMMDD (que corresponden a fechas). Queremos trabajar con ellos pero les queremos quitar el prefijo y el sufijo para hacer algo solo con la fecha. Podríamos obtener la fecha así:

Mejoras en módulos existentes en la biblioteca estándar

Muchos módulos de la biblioteca estándar han recibido pequeñas mejoras. Quizás, las más directamente relacionadas con este humilde blog son las introducidas en el módulo math.

Tenemos una función mejorada y tres funciones nuevas.

La función mejorada es math.gcd que sirve para calcular el máximo común divisor (greatest common divisor). Hasta ahora solo soportaba dos argumentos y ahora se le pueden introducir un número arbitrario de elementos:

Además, ahora podemos obtener el mínimo común múltiplo usando math.lcm (least common multiple):

Podemos obtener el siguiente valor de coma flotante después de x en la dirección de y. Mejor ver un ejemplo:

Y, por último, tenemos math.ulp, que proviene de Unit in the Last Place (unidad en el último lugar) , que nos devuelve el bit menos significativo:

Módulos nuevos

Tenemos dos módulos nuevos, zoneinfo y graphlib.

El primero, zoneinfo, se detalla en el PEP-0615 y se incluyen las motivaciones para su inclusión y formas de uso. Nos permite definir zonas horarias basadas en la base de datos IANA. Esta información suele venir incluida con muchos sistemas operativos. Si no es así instalará, además un paquete mantenido por los desarrolladores de CPython llamado tzdata. Este paquete será un sustituto natural de pytz. Conviene leer este artículo del desarrollador del módulo zoneinfo (y dateutil) para ver las motivaciones de no usar pytz y empezar a usar zoneinfo. Un ejemplo de uso será:

El segundo módulo, graphlib, puede ser útil en algunos casos concretos. Incluye una clase, TopologicalSorter que permite hacer ordenación topológica. ¿Una ordenación ¡qué!…?. Voy a intentar poner algún ejemplo sin código. Piensa en un grafo acíclico direccionado (Direct Acyclic Graph (DAG)). Es un tipo de grafo que tiene nodos dirigidos y nunca vuelven hacia atrás. Puedes leer sobre ello es este artículo de Ángel Lis sobre grafos. Pues cosas no tan abstractas como, por ejemplo, las asignaturas que debe cursar un estudiante para obtener un título, primero debe aprender cálculo y álgebra, luego ecuaciones diferenciales, luego simulación numérica,… Se podría representar como (yendo la dirección y sentido de arriba a abajo):

Otro ejemplo podría ser una serie de tareas que tienes que realizar pero hay una que comprende varias fases pero hasta que no la termines no puedes continuar con la siguiente. Esto es similar a lo que usa dask para saber qué tiene que calcular en cada momento, qué es un limitante para seguir calculando por una rama del grafo, etc. En este artículo se explica de forma mucho más extendida por si quieres ahondar en ello.

¿Me actualizo ya?

La respuesta corta general es ‘aún no’.

La respuesta larga sería:

  • Actualiza a CPython 3.9.0 si no tienes ninguna dependencia de ninguna biblioteca de terceros y solo usas módulos de la biblioteca estándar y tienes una necesidad imperiosa de probar las novedades.
  • Actualiza a CPython 3.9.0 si eres mantenedora de algún paquete y necesitas empezar a ver si existen conflictos entre tu paquete y la nueva versión de CPython.
  • No actualices aún o espérate a la versión CPython 3.9.1 (¿diciembre o enero?) si necesitas trabajar con librerías de terceros, si necesitas herramientas de trabajo que entiendan como trabajar con algunas de las novedades, etc. Por ejemplo, los desarrolladores de Numpy todavía no tienen instaladores para la versión 3.9.0 porque salió esta semana. Necesitan algo de tiempo para poder tener paquetes disponibles para la versión 3.9.x. Pandas depende de Numpy. hasta que Numpy no tenga paquetes compatibles pandas no podrá tener un paquete compatible,… Si usas un IDE igual las herramientas que integra como los resaltadores de código o los linters, etc, todavía no tienen porque estar listos para entender algunas cosas nuevas de la sintaxis.

Referencias

3 pensamientos sobre “Python 3.9, ¿qué hay de nuevo?”

  1. Los operadores | y |= ya existían para sets, por lo que parece lógico que se emplee también en diccionarios. Lo que me pregunto es si también funcionará |= en python 3.9 con sets e iterables.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

sixty seven − sixty five =