En Python las asignaciones no son expresiones (una expresión es cualquier porción de código que retorne un valor) sino sentencias. Esto implica que, por ejemplo, intentar asignar a una variable el resultado de otra asignación sea un error de sintaxis:
>>> b = (a = 5) File "<stdin>", line 1 b = (a = 5) ^ SyntaxError: invalid syntax
En otros lenguajes, como en C, esto es perfectamente legal. Observemos:
int a, b; b = (a = 5);
Luego de la ejecución de estas dos líneas, a
y b
tienen el valor 5. Esto es porque (a = 5)
es al mismo tiempo una asignación (establece el valor de a
en 5) como una expresión (retorna ese valor asignado).
A menudo esta dualidad nos permite ahorrar algunas líneas de código. Por ejemplo, convertir
int result; result = add(7, 5); if (result > 10) { printf("Resultado: %d\n", result); }
en
int result; if ((result = add(7, 5)) > 10) { printf("Resultado: %d\n", result); }
En Python el mismo código se vería más o menos así:
result = add(7, 5) if result > 10: print("Resultado:", result)
Para simplificar el bloque como lo hicimos en C, Python 3.8 incluye el operador :=
que funciona como asignación y expresión al mismo tiempo.
if (result := add(7, 5)) > 10: print("Resultado:", result)
Esta pequeña introducción de sintaxis no solamente nos permite compactar el código, sino hacerlo más eficiente. Consideremos, por ejemplo, la siguiente lista de listas de números:
list_of_numbers = [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16], [17, 18, 19, 20], [21, 22, 23, 24] ]
Ahora queremos imprimir una nueva lista cuyos elementos sean la suma de cada una de las listas de números, siempre y cuando el resultado sea múltiplo de cinco. Bien podríamos hacer:
# Imprime [10, 90]. print([sum(numbers) for numbers in list_of_numbers if sum(numbers) % 5 == 0])
El problema con esta solución es que sum(numbers)
se ejecuta una vez para cada elemento de list_of_numbers
y dos veces para cada elemento que cumpla la condición. Hasta ahora, la única forma de evitar ese doble cómputo era volver a un bucle tradicional y crear una variable que almacene sum(numbers)
.
new_list = [] for numbers in list_of_numbers: current_sum = sum(numbers) if current_sum % 5 == 0: new_list.append(current_sum) print(new_list)
Pero claro, perdemos la elegancia de la comprensión de listas.
Ahora, a partir de Python 3.8, el nuevo operador permite combinar eficiencia y elegancia en la siguiente solución:
print([current_sum for numbers in list_of_numbers if (current_sum := sum(numbers)) % 5 == 0])
La sintaxis variable := expresión
puede emplearse en cualquier parte del código en donde quepa una expresión.
El documento PEP 572 — Assignment Expressions, que reúne la historia, discusión y sintaxis de las asignaciones como expresiones, ilustra gratamente cómo fue utilizado para la simplificación de porciones de código de la librería estándar. Por ejemplo, parte la función copy.copy()
rezaba así:
reductor = dispatch_table.get(cls) if reductor: rv = reductor(x) else: reductor = getattr(x, "__reduce_ex__", None) if reductor: rv = reductor(4) else: reductor = getattr(x, "__reduce__", None) if reductor: rv = reductor() else: raise Error( "un(deep)copyable object of type %s" % cls)
Introduciendo el nuevo operador obtenemos:
if reductor := dispatch_table.get(cls): rv = reductor(x) elif reductor := getattr(x, "__reduce_ex__", None): rv = reductor(4) elif reductor := getattr(x, "__reduce__", None): rv = reductor() else: raise Error("un(deep)copyable object of type %s" % cls)
Mucho más sencillo, compacto y legible.
Curso online 👨💻
¡Ya lanzamos el curso oficial de Recursos Python en Udemy!
Un curso moderno para aprender Python desde cero con programación orientada a objetos, SQL y tkinter
en 2024.
Consultoría 💡
Ofrecemos servicios profesionales de desarrollo y capacitación en Python a personas y empresas. Consultanos por tu proyecto.