Funciones lambda de Python: funciones anónimas en Python

Desde la versión 1.0 de Python, las funciones lambda se utilizan como medio para la programación funcional. Actualmente, la utilidad de esta función ha sido sustituida en gran medida por otras técnicas. Sin embargo, aún existen algunas áreas de uso especiales que los programadores expertos de Python deberían conocer.

¿Qué son las funciones lambda de Python?

El término “función lambda” significa función anónima en Python. Para crear una función lambda, Python utiliza la palabra clave lambda. Una expresión lambda consiste en la palabra clave lambda seguida de una lista de argumentos, dos puntos y una única expresión (“expression”). En cuanto se llama la función lambda, se proporciona la expresión con los argumentos y se evalúa:

lambda argument: expression

Las funciones son una construcción lingüística fundamental de casi todos los lenguajes de programación y representan la unidad más pequeña de código reutilizable. Normalmente, las funciones en Python se definen con la palabra clave def. Por ejemplo, este también sería el caso de la función square que multiplica un número por sí mismo:

# Define square function
def square(num):
  return num * num
# Show that it works
assert square(9) == 81
python

Además de la forma más conocida de definir funciones en Python mediante la palabra clave def, el lenguaje reconoce las “lambdas”. Estas son funciones breves y anónimas (es decir, sin nombre) que definen una expresión con parámetros. Puedes utilizar las lambdas en cualquier lugar donde se espere una función o se las pueda vincular a un nombre mediante una asignación. Aquí tienes la expresión lambda equivalente a la función square:

# Create square function
squared = lambda num: num * num
# Show that it works
assert squared(9) == 81
python
Nota

En Python, el término “función lambda”se refiere a una función creada con la palabra clave lambda. Lambda no es el nombre de una función y tampoco es uno de los operadores de Python.

¿Cuál es la diferencia entre lambda y def?

Al principio parece extraño que Python reconozca dos formas de crear funciones: lambda y def. Pero hay que tener en cuenta que lambda no es una función propia, sino simplemente una notación diferente para crear funciones cortas localmente. Todas las funciones creadas con lambda pueden crearse también mediante def. Sin embargo, no ocurre lo mismo a la inversa.

A nivel sintáctico, tanto lambda como def son palabras clave. Una diferencia entre ambas radica en la estricta separación que hace Python entre sentencias (“Statement”) y expresiones (“Expression”). Las sentencias son pasos en la ejecución del código, mientras que las expresiones se evalúan frente a un valor.

Con def comienza una sentencia (concretamente un “Compound Statement”) que contiene más sentencias. Dentro de la sentencia def,y solo allí, pueden aparecer sentencias return. Tras la llamada a la función definida con def, una sentencia return devuelve un valor.

A diferencia de la sentencia def, lambda inicia una expresión que no debe contener ninguna sentencia. La expresión lambda toma uno o más argumentos y devuelve una función anónima. Si se llama a la función lambda generada, se hará una evaluación de la expresión contenida con los argumentos pasados y se devolverá el resultado.

¿Cuáles son las limitaciones de las expresiones lambda de Python?

Python limita específicamente la utilidad de las funciones lambda, ya que suele ser mejor nombrar las funciones. Esto obliga a los programadores a pensar en el sentido de la función y a distinguir claramente unas partes de otras.

A diferencia del cuerpo de una función definida mediante la palabra clave def, las lambdas no pueden contener sentencias. Por tanto, no es posible utilizar if, for, etc. dentro de una función lambda. Tampoco es posible lanzar una excepción (Exception), ya que para ello se necesita la sentencia raise.

Las funciones lambda en Python solo pueden contener una única expresión que se evalúa después de llamarla. Dentro de la expresión lambda, no puedes utilizar anotaciones de tipo. Hoy en día se utilizan otras técnicas para la mayoría de los casos de uso de las funciones lambda de Python. Especialmente en las comprensiones.

¿Para qué sirven las funciones lambda de Python?

Las lambdas surgen de la programación funcional. En algunos lenguajes, como JavaScript, a menudo se utilizan funciones anónimas sin una palabra clave especial. En Python, las expresiones lambda se utilizan para crear pequeñas funciones localmente, sin grandes complicaciones. Te mostramos los casos de uso más útiles.

Equipar funciones de orden superior con lambdas en Python

Las lambdas se utilizan a menudo en conexión con funciones de orden superior como map(), filter() y reduce(). Con su ayuda, pueden transformarse los elementos de un “iterable” sin utilizar bucles. Las funciones de orden superior son funciones que toman funciones como parámetros o devuelven una función.

La función map() toma como parámetros una función y un iterable y ejecuta la función para cada elemento del iterable. Considera el problema de generar números cuadrados: utilizamos la función map() y pasamos como argumento una expresión lambda, que genera la función cuadrado. Con map() se aplica la función cuadrado a cada elemento de la lista:

nums = [3, 5, 7]
# Square numbers using using `map()` and `lambda`
squares = map(lambda num: num ** 2, nums)
# Show that it works
assert list(squares) == [9, 25, 49]
python
Nota

Desde Python 3.0, las funciones map() y filter() devuelven un iterable en lugar de una lista. Puedes usar list() dentro de la sentencia assert para descomprimir el iterable en una lista.

Con las comprensiones de listas, ahora existe un enfoque moderno y preferido para procesar iterables. En lugar de recurrir a map() y crear una función lambda, puede describirse la operación directamente:

nums = [3, 5, 7]
# Square numbers using list comprehension
squares = [num ** 2 for num in nums]
# Show that it works
assert squares == [9, 25, 49]
python

Con la función filter() pueden filtrarse los elementos de un iterable. Ampliamos nuestro ejemplo para que solo se generen números cuadrados pares:

# List of numbers 1–4
nums = [1, 2, 3, 4]
# Square each number
squares = list(map(lambda num: num ** 2, nums))
# Filter out the even squares
even_squares = filter(lambda square: square % 2 == 0, squares)
# Show that it works
assert list(even_squares) == [4, 16]
python

Volvemos a mostrar el enfoque moderno y preferido de utilizar la comprensión de listas para generar el mismo resultado sin utilizar lambdas ni funciones de orden superior. Utilizamos la parte if de la comprensión para filtrar los números pares de los números cuadrados:

# List of numbers 1–4 squared
squares = [num ** 2 for num in range(1, 5)]
# Filter out the even squares
even_squares = [square for square in squares if square % 2 == 0]
# Show that it works
assert even_squares == [4, 16]
python
Nota

La función reduce() de Python ya no forma parte de la biblioteca estándar desde Python 3.0. Se ha trasladado al módulo functools.

Realizar funciones clave con lambdas en Python

Las comprensiones han sustituido en gran medida el uso de las clásicas funciones de orden superior map() y filter() en Python. Sin embargo, con las “Key-Functions”, existe un escenario de uso en el que las lambdas muestran plenamente sus puntos fuertes.

Las funciones de comparación de Python sorted(), min() y max() operan sobre iterables. Cuando se llama esta función, se compara cada elemento del iterable. Las tres funciones toman una función clave como parámetro opcional key. La función clave se hace cargo de cada elemento y devuelve un valor clave para la operación de comparación.

Como ejemplo, considera el siguiente problema. Imagina que tienes una carpeta con archivos de imagen cuyos nombres están en una lista de Python. Quieres ordenar la lista. Todos los nombres de archivo empiezan por img, seguido de una numeración:

# List of image file names
images = ['img1', 'img2', 'img30', 'img3', 'img22', 'img100']
python

Si usas la función sorted() de Python, se utiliza el “orden lexicográfico”. Esto significa que los dígitos consecutivos se tratan como números únicos. De esta manera, los números ['1', '2', '100'] se colocan en el orden ['1', '100', '2']. Es decir, el resultado no cumple con las expectativas:

# Sort using lexicographic order
sorted_image = sorted(images)
# Show that it works
assert sorted_image == ['img1', 'img100', 'img2', 'img22', 'img3', 'img30']
python

Para que el orden se adapte a tus necesidades, usamos una expresión lambda que crea una función clave. Esta permite extraer la parte numérica de un nombre de archivo para que sorted() la utilice como clave:

# Extract numeric component and sort as integers
sorted_image = sorted(images, key=lambda name: int(name[3:]))
# Show that it works
assert sorted_image == ['img1', 'img2', 'img3', 'img22', 'img30', 'img100']
python

La función clave solo se utiliza localmente y una sola vez. No necesitas definir una función con nombre adicional para ella. Por esto, las lambdas son el medio adecuado para crear funciones clave. Veamos otros dos ejemplos.

Además de sorted(), las funciones incorporadas de Python min() y max() toman una función clave opcional. Las funciones encuentran el elemento más pequeño o más grande de una lista u otro iterable. Qué constituye exactamente el elemento más pequeño o más grande es una cuestión de definición y puede especificarse mediante la función clave.

Con listas de valores simples, por ejemplo una lista de números, está claro lo que se entiende por elemento “más pequeño” o “más grande”. Aquí no se necesita una función clave especial:

nums = [42, 69, 51, 13]
assert min(nums) == 13
assert max(nums) == 69
python
Nota

Si no se pasa ninguna función clave, se utiliza implícitamente la función de identidad f(x) = x. Esta se puede definir fácilmente como una lambda de Python con lambda x: x.

Pero ¿qué ocurre si los elementos de un iterable comprenden cada uno varias fechas? Imagina una lista de diccionarios (“dicts”) que representan personas con nombre y edad. ¿Según qué criterio deberían min() y max() decidir qué elemento es el más pequeño o el más grande? Para esto se utiliza exactamente la función clave.

Para ilustrar cómo funcionan las funciones clave, necesitamos datos de ejemplo. Por lo tanto, crea una función Person() que sirve de constructor:

# Constructor function for dict representing a person
def Person(name, age):
  return {'name': name, 'age': age}
# Check that it works as expected
assert Person('Jim', 42) == {'name': 'Jim', 'age': 42}
python

Con ayuda de la función constructora, crea una lista de personas:

# Create list of people
people = [Person('Jim', 42), Person('Jack', 51), Person('John', 69)]
python

A continuación, busca a la persona de más edad mediante la llamada max(). Una vez que hayas realizado este paso, se genera una función clave mediante una expresión lambda que toma un dict de persona y extrae de él la edad como elemento de comparación:

# Find the oldest person
oldest = max(people, key=lambda person: person['age'])
# Check that it works
assert oldest == Person('John', 69)
python

El planteamiento es exactamente el mismo para la función min(). Aquí hay que definir la función clave fuera de la llamada min() utilizando, nuevamente, una expresión lambda. Esto mejora la legibilidad y merece la pena si la función clave tiene múltiples usos locales:

# Define key function to compare people by age
by_age = lambda person: person['age']
# Find the youngest person
youngest = min(people, key=by_age)
# Check that it works
assert youngest == Person('Jim', 42)
python

Creación de closures con lambdas de Python

Otro uso de las lambdas de Python es la definición de los “closures”, funciones que son creadas por otras funciones y almacenan un valor. Los closures pueden utilizarse, por ejemplo, para crear familias de funciones similares. A continuación, mostramos el ejemplo habitual: crear funciones de potencia.

Las funciones de potencia toman un argumento y lo exponencian. Ejemplos conocidos son la función cuadrada f(x) = x ^ 2 y la función cúbica f(x) = x ^ 3. Mediante una función constructora, se puede generar cualquier función potencia como closure. Utiliza una expresión lambda y te ahorras así la definición de una función interna con nombre:

# Define constructor function for power functions
def power(n):
  return lambda num: num ** n

# Create square and cubic functions as closures
square = power(2)
cubic = power(3)

# Show that it works
assert square(10) == 100
assert cubic(10) == 1000
python

Immediately Invoked Function Expression (IIFE) con lambdas en Python

IIFE, pronunciado “iffy”, es un patrón conocido en JavaScript. Define una función anónima y la ejecuta inmediatamente.

Aunque no son muy útiles en Python debido a su restricción de uso, las lambdas pueden utilizarse como IIFE. Solo se necesitan paréntesis alrededor de la expresión lambda:

(lambda num: num * num)
python

Adicionalmente, otro par de paréntesis que contengan el argumento o argumentos:

assert (lambda num: num * num)(3) == 9
python
Consejo

Si quieres aprender más sobre Python, te recomendamos nuestro tutorial de Python.

Utilizamos cookies propias y de terceros para mejorar nuestros servicios y mostrarle publicidad relacionada con sus preferencias mediante el análisis de sus hábitos de navegación. Si continua navegando, consideramos que acepta su uso. Puede obtener más información, o bien conocer cómo cambiar la configuración de su navegador en nuestra. Política de Cookies.