Course
Operador Morsa de Python: La expresión de asignación (Tutorial)
Python 3.8 introdujo un nuevo operador operador al lenguaje. Su nombre oficial esexpresión de asignación , y utiliza el operador :=
.
Sin embargo, es más común llamarlo operador morsa ¡ya que se parece a las tareas de una morsa!
Hubo controversia en el normalmente tranquilo mundo Python cuando se añadió este operador al lenguaje, y su uso sigue siendo relativamente limitado. Aun así, la expresión de asignación ahora forma parte de Python, y puede ser útil en algunas situaciones.
Este tutorial explorará cómo funciona el operador morsa de Python y analizará situaciones en las que se puede utilizar.
¿Qué es el Operador Morsa de Python?
La expresión de asignación expresión de asignaciónque utiliza el operador :=
, tiene un aspecto similar a la más común sentencia de asignaciónque utiliza el operador =
. Sin embargo, hay una distinción importante entre ambos.
A sentencia es una unidad de código que realiza alguna acción. Un expresión es una expresión que puede evaluarse para obtener un valor. Por tanto, una expresión devuelve un valor, mientras que las declaraciones que no son expresiones no devuelven ningún valor.
El REPL de Python (Leer, Evaluar, Imprimir y Bucle) muestra el valor de una expresión en cuanto se evalúa. Sin embargo, no se muestra ningún valor al ejecutar una sentencia que no sea una expresión:
>>> import random # Not an expression
>>> 10 * 2 # An expression
20
La declaración import
no se evalúa a ningún valor. Sin embargo, 10 * 2
es una expresión que se evalúa a un valor.
La sentencia de asignación, que utiliza el operador =
, asigna un objeto a una variable o a otra referencia, pero no devuelve el valor. La expresión de asignación, u operador morsa, realiza la misma acción que la sentencia de asignación, pero además devuelve el objeto:
>>> value = 10 # Assignment statement
>>> value
10
>>> (value := 20) # Assignment expression
20
Ten en cuenta que la sentencia de asignación no devuelve el valor, a diferencia de la expresión de asignación. Por tanto, una expresión de asignación combina varias operaciones:
- Evalúa la expresión a la derecha del operador
:=
. - Asigna este valor a un nombre de variable.
- Devuelve el valor.
Los paréntesis son sintaxis obligatoria cuando se utiliza la expresión de asignación en lugar de una sentencia de asignación, como en este ejemplo, para reducir la ambigüedad. Más adelante en este tutorial encontrarás más información sobre los requisitos sintácticos del operador morsa.
Una expresión de asignación se puede utilizar en cualquier lugar donde se puedan utilizar expresiones en Python. Veamos un ejemplo:
print(f"The number is {(value := 50)}")
print(value)
The number is 50
50
El nombre de la variable value
se define dentro de cadena-f en la primera línea. Sin embargo, el número entero también se devuelve directamente a las llaves de la cadena f. Las llaves de las cadenas f deben incluir una expresión, lo que explica por qué no se puede utilizar una sentencia de asignación.
Casos prácticos del operador morsa de Python
Dos casos de uso común del operador morsa son la simplificación y la optimización del código.
Simplificar el código
Un caso de uso habitual del operador morsa es simplificar el código en el que normalmente habría que definir una variable antes de utilizarla, como en algunas sentencias condicionales. Considera el siguiente bucle while
:
import random
value = random.randint(1, 20)
while value < 18:
print(value)
value = random.randint(1, 20)
7
5
1
11
12
1
9
Este código ejecuta el bloque while
siempre que el valor aleatorio generado sea menor que 18
. La variable value
debe definirse antes del bucle while
para que pueda utilizarse en la declaración while
. Se asigna un nuevo número aleatorio a value
al final del bloque while
.
La expresión de asignación :=
puede utilizarse para reducir las líneas de código y llamar a la función random.randint()
en un único lugar del programa:
import random
while (value := random.randint(1, 20)) < 18:
print(value)
1
9
2
4
9
15
11
Se genera un nuevo número aleatorio y se asigna a value
cada vez que el bucle while
inicia una nueva iteración.
Optimizar el código
Veamos otro ejemplo utilizando comprensiones de listas. Considera el siguiente código, que filtra una lista de cadenas que contienen fechas y crea una lista de datetime.datetime
objetos que sólo contengan fechas de un año determinado:
from datetime import datetime
def format_date(date_str):
return datetime.strptime(date_str, "%Y-%m-%d")
dates = ["2024-01-01", "2022-12-31", "2024-06-15", "2023-08-23", "2024-11-30"]
formatted_dates = [
format_date(date)
for date in dates
if format_date(date).year == 2024
]
print(formatted_dates)
[datetime.datetime(2024, 1, 1, 0, 0), datetime.datetime(2024, 6, 15, 0, 0), datetime.datetime(2024, 11, 30, 0, 0)]
La función format_date()
acepta una cadena con una fecha en el formatoaaaa-mm-dd y devuelve un objeto datetime.datetime
. Uno de los atributos del objeto datetime.datetime
es .year
, que contiene el componente año de la fecha.
Sólo las tres fechas de 2024 están incluidas en la lista final, que también contiene objetos datetime.datetime
en lugar de cadenas. La comprensión de la lista incluye una cláusula if
, que llama a format_date()
. Sin embargo, también se llama a format_date()
en la primera expresión de la comprensión de la lista, que genera los valores que hay que añadir a la lista. Llamar dos veces a la función con el mismo argumento es ineficaz, sobre todo si la función es un cuello de botella en el rendimiento del programa.
Una opción para evitar llamar dos veces a la misma función es llamar a la función y asignarla a una variable antes de utilizarla. Sin embargo, esto no puede conseguirse con una sentencia de asignación mientras se sigue utilizando una comprensión de lista. Esta solución requiere un bucle estándar for
:
from datetime import datetime
def format_date(date_str):
return datetime.strptime(date_str, "%Y-%m-%d")
dates = ["2024-01-01", "2022-12-31", "2024-06-15", "2023-08-23", "2024-11-30"]
formatted_dates = []
for date in dates:
formatted_date = format_date(date)
if formatted_date.year == 2024:
formatted_dates.append(formatted_date)
print(formatted_dates)
[datetime.datetime(2024, 1, 1, 0, 0), datetime.datetime(2024, 6, 15, 0, 0), datetime.datetime(2024, 11, 30, 0, 0)]
El operador morsa proporciona una alternativa compatible con la comprensión de una lista, ya que la fecha formateada devuelta por la función puede asignarse a un nombre de variable y devolverse en la misma expresión:
from datetime import datetime
def format_date(date_str):
return datetime.strptime(date_str, "%Y-%m-%d")
dates = ["2024-01-01", "2022-12-31", "2024-06-15", "2023-08-23", "2024-11-30"]
formatted_dates = [
formatted_date
for date in dates
if (formatted_date := format_date(date)).year == 2024
]
print(formatted_dates)
[datetime.datetime(2024, 1, 1, 0, 0), datetime.datetime(2024, 6, 15, 0, 0), datetime.datetime(2024, 11, 30, 0, 0)]
La expresión de asignación en la comprensión de la lista es la expresión encerrada entre paréntesis, (formatted_date := format_date(date))
. Esta expresión llama a la función format_date()
, asigna su valor a la variable formatted_date
, y devuelve el valor. Como la expresión de asignación evalúa a un objeto datetime.datetime
, el código accede al atributo .year
, que se compara con 2024
mediante el operador de igualdad.
La cláusula if
de la comprensión de la lista se ejecuta antes de la expresión que genera el valor a almacenar en la lista. Por tanto, se utiliza el operador morsa en la cláusula if
, y la variable definida en esta expresión de asignación se utiliza como primera expresión en la comprensión de la lista.
Polémica sobre el Operador de Morsas de Python
La expresión de asignación no es querida por todos los programadores de Python. Algunos lo ven como una forma de simplificar el código, pero otros creen que lo hace menos legible y prefieren las opciones más largas. La legibilidad puede ser subjetiva, por lo que las opiniones varían sobre si el operador morsa hace que el código sea más o menos legible.
El último ejemplo de la sección anterior muestra las compensaciones necesarias. Sin el operador morsa, el código de este ejemplo necesita que se inicialice primero una lista vacía, y luego se le añaden valores en un bucle for
. Esta solución es más larga y menos eficaz que utilizar comprensiones de listas, que están optimizadas para este trabajo.
Sin embargo, la solución estándar del bucle for
puede ser más legible para algunos programadores que prefieren escribir más código en lugar de utilizar el operador morsa, sobre todo si el rendimiento no es una preocupación.
Muchas guías de estilo de codificación siguen recomendando evitar la expresión de asignación o utilizarla con moderación.
Operador Morsa de Python: Reglas sintácticas
Es habitual encontrar errores de sintaxis al explorar inicialmente el operador morsa debido a sus reglas sintácticas.
Para evitar confusiones entre la sentencia de asignación =
y la expresión de asignación :=
, no hay ninguna situación en la que ambas opciones sean sintácticamente válidas. Por esta razón, a menudo es necesario encerrar la expresión de asignación entre paréntesis:
(value := 20) # Valid
value := 20 # Invalid (SyntaxError)
El requisito de incluir la expresión de asignación entre paréntesis en esta situación es lo contrario del requisito de la sentencia de asignación, que no puede incluirse entre paréntesis.
Sin embargo, la expresión de asignación no siempre necesita paréntesis:
import random
if value := random.randint(0, 3):
print(f"{value} is greater than 0")
1 is greater than 0
La expresión de asignación de la sentencia if
genera un número aleatorio entre 0 y 3 y lo asigna a la variable value
. Los enteros 1
, 2
, y 3
son verdaderosy el código del bloque if
se ejecutará cuando se generen estos valores. Sin embargo, cuando random.randint()
devuelve 0
, que es falso, el bloque if
no se ejecuta.
La sentencia de asignación no es válida después de una palabra clave if
. Por tanto, no hay confusión en esta situación, y los paréntesis no son necesarios.
Este ejemplo también demuestra otro punto clave sobre la expresión de asignación. El ámbito de la variable asignada sigue las reglas normales reglas LEGB. Por lo tanto, la variable value
está disponible en todo el ámbito global en este ejemplo:
import random
if value := random.randint(0, 3):
print(f"{value} is greater than 0")
print(value)
2 is greater than 0
2
Si se utiliza una expresión de asignación dentro de una definición de función, la variable sólo estará disponible localmente dentro de la función. Éste es el mismo comportamiento que para las variables creadas mediante una sentencia de asignación.
Este ejemplo puede modificarse para mostrar otro posible escollo al utilizar el operador morsa. Cambiemos este código para incluir un operador de comparación en la declaración if
:
import random
if value := random.randint(0, 3) < 2:
print(f"{value} is less than 2")
True is less than 2
La salida mostrada indica el problema con este código, ya que value
es el booleano True
en lugar de un entero. El operador morsa tiene la precedencia más baja precedencia de todos los operadoresy sólo la coma tiene menor precedencia. Por tanto, primero se evalúa el operador menor que <
y su resultado se asigna a value
. Los paréntesis son necesarios para hacer frente a esta situación:
import random
if (value := random.randint(0, 3)) < 2:
print(f"{value} is less than 2")
0 is less than 2
Ahora se evalúa primero la expresión de asignación, ya que los paréntesis tienen mayor precedencia. El número devuelto por random.randint()
se asigna a value
, y se utiliza como primer operando del operador menor que.
A diferencia de la sentencia de asignación, el operador morsa sólo puede asignar valores a nombres de variables y no puede utilizarse para asignar valores a subíndices o atributos:
(name := "James") # Valid
points = {"James": 10, "Mary": 20}
(points["Sarah"] := 30) # Invalid
class Article:
pass
article = Article()
(article.title := "The Walrus Operator") # Invalid
No es posible asignar un valor mediante un subíndice ni a un atributo mediante el operador morsa.
Conclusión
La expresión de asignación, que utiliza la sintaxis :=
y a menudo se denomina operador morsa, es una adición relativamente nueva a Python. La sentencia de asignación, que utiliza el operador =
, es la forma más habitual de asignar un valor a un nombre de variable.
Sin embargo, el operador morsa amplía esta operación de asignación devolviendo también el valor asignado, lo que convierte esta asignación en una expresión. Por tanto, puedes utilizar la expresión de asignación siempre que puedas utilizar otras expresiones en Python.
Sin embargo, la comunidad Python está dividida sobre la utilidad y legibilidad del operador morsa. Por esta razón, la expresión de asignación no se utiliza tan a menudo como otras nuevas incorporaciones a Python. Está muy bien saber utilizar este operador, ¡pero no abuses de él!
Aprende Python con estos cursos
Course
Intermediate Python for Developers
Course
Introduction to Statistics in Python
tutorial
Alcance de las variables en Python
tutorial
Función del guión bajo(_) en el tutorial de Python
tutorial
Tutorial de funciones de Python
tutorial
Tutorial de Python sobre conjuntos y teoría de conjuntos
DataCamp Team
13 min
tutorial
Programación orientada a objetos (POO) en Python: Tutorial
tutorial