Última actualización: 04 de diciembre de 2023

5.5. Funciones de orden superior

Las funciones de Python pueden tomar funciones como parámetros y devolver funciones como resultado. Una función que hace ambas cosas o alguna de ellas se llama función de orden superior.

5.5.1. filter()

La función filter() es una función la cual toma un predicado y una lista y devuelve una lista con los elementos que satisfacen el predicado. Tal como su nombre indica filter() significa filtrar, ya que a partir de una lista o iterador y una función condicional, es capaz de devolver una nueva colección con los elementos filtrados que cumplan la condición.

Todo esto podría haberse logrado también usando listas por comprensión que usaran predicados. No hay ninguna regla que diga cuando usar la función map() o la función filter() en lugar de las listas por comprensión, simplemente debe decidir que es más legible dependiendo del contexto.

Por ejemplo, suponga que tiene una lista varios números y requiere filtrarla, quedando únicamente con los números múltiples de 5, eso seria así:

>>> # Primero declaramos una función condicional
>>> def multiple(numero):
...     # Comprobamos si un numero es múltiple de cinco
...     if numero % 5 == 0:
...         # Sólo devolvemos True si lo es
...         return True
...

>>> numeros = [2, 5, 10, 23, 50, 33]
>>> for numero in filter(multiple, numeros):
...     print(numero)
...
5
10
50
>>>

Si ejecuta el filtro obtiene una lista los números múltiples de 5. Por tanto cuando utiliza la función filter() tiene que enviar una función condicional, para esto, puede utilizar una función anónima lambda, como se muestra a continuación:

>>> numeros = [2, 5, 10, 23, 50, 33]
>>> for i in filter(lambda numero: numero % 5 == 0, numeros):
...     print(i)
...
5
10
50
>>>

Así, en una sola línea ha definido y ejecutado el filtro utilizando una función condicional anónima y devolviendo una lista de números.

5.5.1.1. Filtrando objetos

Sin embargo, más allá de filtrar listas con valores simples, el verdadero potencial de la función filter() sale a relucir cuando usted necesita filtrar varios objetos de una lista.

Por ejemplo, dada una lista con varias personas, a usted le gustaría filtrar únicamente las cuales son menores de edad:

>>> class Persona:
...     def __init__(self, nombre, edad):
...         self.nombre = nombre
...         self.edad = edad
...     def __str__(self):
...         return "{} de {} años".format(self.nombre, self.edad)
...
>>> personas = [
...     Persona("Leonardo", 38),
...     Persona("Ana", 33),
...     Persona("Sabrina", 12),
...     Persona("Enrique", 3),
... ]
>>> menores = filter(lambda persona: persona.edad < 18, personas)
>>> for menor in menores:
...     print(menor)
...
Sabrina de 12 años
Enrique de 3 años
>>>

Este es un ejemplo sencillo, con el cual usted puede realizar filtrados con objetos, de forma amigable.

5.5.2. map()

La función map() toma una función y una lista y aplica esa función a cada elemento de esa lista, produciendo una nueva lista. Va a ver su definición de tipo y como se define.

Esta función trabaja de una forma muy similar a filter(), con la diferencia que en lugar de aplicar una condición a un elemento de una lista o secuencia, aplica una función sobre todos los elementos y como resultado se devuelve un lista de números doblado su valor:

>>> def doblar(numero):
...     return numero * 2
...
>>> numeros = [2, 5, 10, 23, 50, 33]
>>> map(doblar, numeros)
[4, 10, 20, 46, 100, 66]

Usted puede simplificar el código anterior usando una función lambda para substituir la llamada de una función definida, como se muestra a continuación:

>>> map(lambda x: x * 2, numeros)
[4, 10, 20, 46, 100, 66]

La función map() se utiliza mucho junto a expresiones lambda ya que permite evitar escribir bucles for.

Además se puede utilizar sobre más de un objeto iterable con la condición que tengan la misma longitud. Por ejemplo, si requiere multiplicar los números de dos listas:

>>> a = [1, 2, 3, 4, 5]
>>> b = [6, 7, 8, 9, 10]
>>> map(lambda x, y: x * y, a, b)
[6, 14, 24, 36, 50]

E incluso usted puede extender la funcionalidad a tres listas o más:

>>> a = [1, 2, 3, 4, 5]
>>> b = [6, 7, 8, 9, 10]
>>> c = [11, 12, 13, 14, 15]
>>> map(lambda x, y, z: x * y * z, a, b, c)
[66, 168, 312, 504, 750]

5.5.2.1. Mapeando objetos

Evidentemente, siempre que la función map() la utilice correctamente podrá mapear una serie de objetos sin ningún problema:

>>> class Persona:
...     def __init__(self, nombre, edad):
...         self.nombre = nombre
...         self.edad = edad
...     def __str__(self):
...         return "{} de {} años".format(self.nombre, self.edad)
...
>>> personas = [
...     Persona("Leonardo", 38),
...     Persona("Ana", 33),
...     Persona("Sabrina", 12),
...     Persona("Enrique", 3),
... ]
>>> def incrementar(p):
...     p.edad += 1
...     return p
...
>>> personas = map(incrementar, personas)
>>> for persona in personas:
...     print(persona)
...
Leonardo de 39 años
Ana de 34 años
Sabrina de 13 años
Enrique de 4 años

Claro que en este caso tiene que utilizar una función definida porque no necesitamos actuar sobre la instancia, a no ser que usted se tome la molestia de rehacer todo el objeto:

>>> class Persona:
...     def __init__(self, nombre, edad):
...         self.nombre = nombre
...         self.edad = edad
...     def __str__(self):
...         return "{} de {} años".format(self.nombre, self.edad)
...
>>> personas = [
...     Persona("Leonardo", 38),
...     Persona("Ana", 33),
...     Persona("Sabrina", 12),
...     Persona("Enrique", 3),
... ]
>>> def incrementar(p):
...     p.edad += 1
...     return p
...
>>> personas = map(lambda p: Persona(p.nombre, p.edad + 1), personas)
>>> for persona in personas:
...     print(persona)
...
Leonardo de 39 años
Ana de 34 años
Sabrina de 13 años
Enrique de 4 años

5.5.3. lambda

La expresión lambda, es una función anónima que suelen ser usadas cuando necesita una función una sola vez. Normalmente usted crea funciones lambda con el único propósito de pasarlas a funciones de orden superior.

En muchos lenguajes, el uso de lambdas sobre funciones definidas causa problemas de rendimiento. No es el caso en Python.

>>> import os
>>> archivos = os.listdir(os.__file__.replace("/os.pyc", "/"))
>>> print(filter(lambda x: x.startswith("os."), archivos))
['os.pyc', 'os.py']

En el ejemplo anterior se usa el método os.__file__ para obtener la ruta donde esta instalada el módulo os en su sistema, ejecutando la siguiente sentencia:

>>> os.__file__
'/usr/lib/python3.11/os.pyc'

Si con el método os.__file__ obtiene la ruta del módulo os con el método replace("/os.pyc", "/") busca la cadena de carácter «/os.pyc» y la remplaza por la cadena de carácter «/»

>>> os.__file__.replace("/os.pyc", "/")
'/usr/lib/python3.11/'

Luego se define la variable archivos generando una lista de archivos usando la función os.listdir(), pasando el parámetro obtenido de la ruta donde se instalo el módulo os ejecutando en el comando previo, con la siguiente sentencia:

>>> archivos = os.listdir("/usr/lib/python3.11/")

De esta forma se define en la variable archivos un tipo lista con un tamaño de 433, como se puede comprobar a continuación:

>>> type(archivos)
<type 'list'>
>>> len(archivos)
443

Opcionalmente puede comprobar si la cadena de caracteres os.pyc se encuentras una de las posiciones de la lista archivos, ejecutando la siguiente sentencia:

>>> "os.pyc" in archivos
True

Ya al comprobar que existe la cadena de caracteres «os.pyc» se usa una función lambda como parámetro de la función filter() para filtrar todos los archivos del directorio «/usr/lib/python3.11/» por medio del función os.listdir() que inicien con la cadena de caracteres «os.» usando la función startswith().

>>> print(filter(lambda x: x.startswith("os."), os.listdir("/usr/lib/python3.11/")))
['os.pyc', 'os.py']

Así de esta forma se comprueba que existe el archivo compilado «os.pyc» de Python junto con el mismo módulo Python «os.py».

Truco

Más detalle consulte la referencia de las expresiones lambda.


Ver también

Consulte la sección de lecturas suplementarias del entrenamiento para ampliar su conocimiento en esta temática.


¿Cómo puedo ayudar?

¡Mi soporte está aquí para ayudar!

Mi horario de oficina es de lunes a sábado, de 9 AM a 5 PM. GMT-4 - Caracas, Venezuela.

La hora aquí es actualmente 7:35 PM GMT-4.

Mi objetivo es responder a todos los mensajes dentro de un día hábil.

Contrata mi increíble soporte profesional