Curso
Una necesidad habitual en programas de Python es comprobar si al menos un elemento de una estructura de datos es True.
Aunque puedes escribir un bucle para iterar por un iterable y comprobar si cada elemento se evalúa como True o False, la función any() de Python es una forma más eficiente y "pythónica" de conseguirlo.
Este tutorial explora la función integrada any() de Python y muestra varios ejemplos y casos de uso.
Perfeccionamiento en IA para principiantes
¿Qué hace la función any() de Python?
La any() es una de las funciones integradas de Python. Acepta un iterable como una lista o una tupla, y devuelve True si al menos un valor del iterable es truthy. Un valor "truthy" se evalúa como True al convertirlo a booleano con bool(). Si todos los valores del iterable son "falsy", any() devuelve False:
print(any([True, False, False, True]))
print(any([False, False, False, False]))
print(any([0, 0, 5, 0]))
True
False
True
El iterable puede contener valores booleanos u otros objetos, ya que todos los objetos se evalúan como True o False al convertirlos a booleano. En el tercer ejemplo, la lista incluye cuatro enteros. El entero 0 es "falsy", pero todos los números distintos de cero son "truthy". Para entender mejor la diferencia entre valores "truthy" y "falsy", lee este artículo sobre operadores lógicos en Python.
Si el iterable está vacío, any() devuelve False porque no hay valores "truthy" en un iterable vacío:
print(any([]))
False
En la siguiente sección profundizamos en cómo funciona any().
¿Cómo funciona any()?
La función integrada any() acepta cualquier iterable como argumento. Un iterable es cualquier estructura de datos sobre la que Python puede iterar.
Considera la siguiente tupla:
an_iterable = ("", 0.0, "DataCamp", False, [])
La tupla incluye varios objetos de distintos tipos. Varios de estos objetos son "falsy":
- La cadena vacía
"" - El float
0.0 - El booleano
False - La lista vacía
[]
Estos objetos son "falsy" porque se evalúan como False al pasarlos a bool().
La cadena no vacía "DataCamp" es "truthy".
Una forma directa de comprobar si al menos un elemento es "truthy" es usar un bucle for:
def check_if_contains_truthy(data):
output = False
for item in data:
if item:
output = True
return output
print(check_if_contains_truthy(an_iterable))
True
Sin embargo, esto es ineficiente porque la función solo necesita comprobar si hay al menos un valor "truthy". En cuanto encuentra uno, no hace falta seguir comprobando el resto de elementos del iterable. Podemos modificar la función para que devuelva en cuanto encuentre el primer valor "truthy":
def check_if_contains_truthy(data):
for item in data:
if item:
return True
return False
print(check_if_contains_truthy(an_iterable))
True
Una sentencia return detiene la ejecución de la función. Por tanto, cuando la función encuentra el primer valor "truthy", devuelve True y no continúa iterando. La función devuelve False solo si el bucle for recorre todos los elementos del iterable y ninguno es "truthy".
La función integrada any() sigue un patrón similar y hace "short-circuit" cuando encuentra el primer valor "truthy". Esta evaluación perezosa evita operaciones innecesarias y mejora el rendimiento. La any() de Python es una versión más eficiente de la función anterior. Esta mejora se confirma con la función timeit() del módulo timeit, que mide cuánto tarda Python en ejecutar una sentencia:
from timeit import timeit
print("Execution time for 'check_if_contains_truthy()'")
print(
timeit(
"check_if_contains_truthy(an_iterable)",
number=10_000_000,
globals=globals(),
)
)
print("Execution time for 'any()'")
print(
timeit(
"any(an_iterable)",
number=10_000_000,
globals=globals(),
)
)
Execution time for 'check_if_contains_truthy()'
0.6335119580035098
Execution time for 'any()'
0.39488654200249584
El primer argumento de timeit() es una cadena con la sentencia a ejecutar. El segundo argumento determina cuántas veces se ejecuta. En este ejemplo, ambas se ejecutan 10.000.000 de veces para obtener tiempos significativos. El último argumento pasa las variables globales a la función timeit().
La any() de Python es más rápida que check_if_contains_truthy(). Usar any() para saber si un iterable tiene al menos un valor "truthy" es más "pythónico" y eficiente que usar un bucle for.
Casos de uso de any()
Veamos algunos casos de uso de any() en Python.
Comprobar si al menos una entrada de usuario es válida
Imagina una serie de entradas de usuario recogidas en una lista o tupla. Un programa puede necesitar comprobar si al menos una de las entradas es válida, por ejemplo, si es una cadena no vacía:
user_inputs = ["", "10.99", ""]
if any(user_inputs):
print("At least one valid input present")
# Rest of code...
else:
print("There are no valid inputs")
At least one valid input present
Como una de las cadenas no está vacía, any() devuelve True y se ejecuta el código del bloque if.
Comprobar si existe al menos un archivo
La función integrada any() puede usarse para comprobar si al menos uno de los archivos de una lista de nombres existe en un directorio:
from pathlib import Path
files = ["article.md", "use_any.py", "scratch.py", "test_file.txt"]
cwd = Path.cwd()
# Create a test file to test code
Path("test_file.txt").write_text("This is a test file")
if any(Path(file).exists() for file in files):
print("At least one of the files exists")
# Rest of code…
else:
print("None of the files exist")
At least one of the files exists
El código obtiene el directorio de trabajo actual usando el módulo pathlib y Path.cwd(). Crea un archivo llamado test_file.txt en el directorio de trabajo para facilitar la prueba. Se usa any() para comprobar si al menos uno de los nombres de archivo existe en el directorio de trabajo actual.
Filtrar elementos de un iterable
La función integrada any() también puede usarse con una expresión generadora como argumento y filtrar elementos de un iterable. En este ejemplo, any() busca al menos una entrada en una lista de diccionarios que representa a una persona cuya edad es menor que 18:
records = [
{"name": "Sarah", "age": 23},
{"name": "Jake", "age": 30},
{"name": "Paul", "age": 17},
]
if any(record["age"] < 18 for record in records):
print("There's at least one minor.")
else:
print("All records are adults.")
There's at least one minor.
La expresión generadora produce True o False según el valor asociado a la clave "age" de cada diccionario. Una expresión generadora puede usarse en any() porque es iterable.
Revisitando la evaluación perezosa en any() de Python
El ejemplo anterior también puede demostrar que any() se evalúa de forma perezosa. Considera la siguiente modificación de la lista de diccionarios:
records = [
{"name": "Sarah", "age": 23},
{"name": "Jake", "age": 30},
{"name": "Paul", "age": 17},
{"name": "Jill"},
]
if any(record["age"] < 18 for record in records):
print("There's at least one minor.")
else:
print("All records are adults.")
There's at least one minor.
El cuarto diccionario de records no contiene un elemento con la clave "age". Sin embargo, el código no lanza un error al intentar obtener el valor correspondiente a "age" para este elemento porque any() encontró antes un valor "truthy". La función hace "short-circuit" cuando encuentra el primer valor "truthy".
El código lanzará un KeyError si el diccionario que no tiene la clave "age" aparece antes que cualquier elemento con una edad inferior a 18:
records = [
{"name": "Jill"},
{"name": "Sarah", "age": 23},
{"name": "Jake", "age": 30},
{"name": "Paul", "age": 17},
]
if any(record["age"] < 18 for record in records):
print("There's at least one minor.")
else:
print("All records are adults.")
Traceback (most recent call last):
...
if any(record["age"] < 18 for record in records):
~~~~~^^^^^^
KeyError: 'age'
Este ejemplo está diseñado para mostrar el comportamiento de "short-circuit" al usar any() en Python. Hay una alternativa si no quieres que el código lance excepciones para elementos sin la clave "age". Puedes usar el método de diccionario .get() con un valor por defecto en lugar de acceder con corchetes. Si el comportamiento por defecto es asumir que las entradas sin edad no son menores, puedes sustituir la sentencia if por la siguiente versión:
if any(record.get("age", 18) < 18 for record in records):
# ...
Si el código debe asumir que cualquier entrada sin edad es menor, el valor por defecto tendrá que ser inferior a 18.
Python: any() vs. all()
La any() de Python tiene una función complementaria, all(). Esta función integrada también acepta un iterable y devuelve True si todos los elementos del iterable son "truthy".
Recuperemos un ejemplo anterior del tutorial para validar entradas de usuario. any() es útil si la aplicación requiere al menos una entrada válida. En cambio, all() sirve si todas las entradas deben ser válidas:
user_inputs = ["", "10.99", ""]
if any(user_inputs):
print("At least one valid input present")
# Rest of code...
else:
print("There are no valid inputs")
if all(user_inputs):
print("All inputs are valid")
# Rest of code...
else:
print("Not all inputs are valid")
At least one valid input present
Not all inputs are valid
Sin embargo, si todas las cadenas de las entradas de usuario no están vacías, all() devuelve True:
user_inputs = ["5.99", "10.99", "19.99"]
# …
At least one valid input present
All inputs are valid
Al igual que any(), la función integrada all() se evalúa de forma perezosa. La función hace "short-circuit" cuando encuentra el primer valor "falsy".
Conclusión
La función any() de Python comprueba si alguno de los elementos de un iterable es "truthy". Devuelve True si al menos un elemento del iterable es True o se evalúa como True al convertirlo a booleano. Aunque podrías lograr lo mismo con un bucle, esta función integrada ofrece una solución más legible y eficiente.
Obtén una certificación superior en IA
Estudié Física y Matemáticas a nivel UG en la Universidad de Malta. Después me trasladé a Londres y me doctoré en Física en el Imperial College. Trabajé en novedosas técnicas ópticas para obtener imágenes de la retina humana. Ahora, me centro en escribir sobre Python, comunicar sobre Python y enseñar Python.
