tl; dr
Una expresión generadora es probablemente la solución más eficaz y sencilla a su problema:
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
result = next((i for i, v in enumerate(l) if v[0] == 53), None)
# 2
Explicación
Hay varias respuestas que brindan una solución simple a esta pregunta con listas por comprensión. Si bien estas respuestas son perfectamente correctas, no son óptimas. Dependiendo de su caso de uso, puede haber beneficios significativos al realizar algunas modificaciones simples.
El principal problema que veo con el uso de una lista de comprensión para este caso de uso es que se procesará la lista completa , aunque solo desea encontrar 1 elemento .
Python proporciona una construcción simple que es ideal aquí. Se llama expresión generadora . Aquí hay un ejemplo:
# Our input list, same as before
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
# Call next on our generator expression.
next((i for i, v in enumerate(l) if v[0] == 53), None)
Podemos esperar que este método funcione básicamente de la misma manera que las listas por comprensión de nuestro ejemplo trivial, pero ¿qué pasa si estamos trabajando con un conjunto de datos más grande? Ahí es donde entra en juego la ventaja de utilizar el método del generador. En lugar de construir una nueva lista, usaremos su lista existente como nuestro iterable, y usaremos next()
para obtener el primer elemento de nuestro generador.
Veamos cómo estos métodos funcionan de manera diferente en algunos conjuntos de datos más grandes. Estas son listas grandes, hechas de 10000000 + 1 elementos, con nuestro objetivo al principio (mejor) o al final (peor). Podemos verificar que ambas listas funcionarán por igual usando la siguiente comprensión de listas:
Lista de comprensiones
"Peor de los casos"
worst_case = ([(False, 'F')] * 10000000) + [(True, 'T')]
print [i for i, v in enumerate(worst_case) if v[0] is True]
# [10000000]
# 2 function calls in 3.885 seconds
#
# Ordered by: standard name
#
# ncalls tottime percall cumtime percall filename:lineno(function)
# 1 3.885 3.885 3.885 3.885 so_lc.py:1(<module>)
# 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
"Mejor caso"
best_case = [(True, 'T')] + ([(False, 'F')] * 10000000)
print [i for i, v in enumerate(best_case) if v[0] is True]
# [0]
# 2 function calls in 3.864 seconds
#
# Ordered by: standard name
#
# ncalls tottime percall cumtime percall filename:lineno(function)
# 1 3.864 3.864 3.864 3.864 so_lc.py:1(<module>)
# 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Expresiones generadoras
Aquí está mi hipótesis para los generadores: veremos que los generadores funcionarán significativamente mejor en el mejor de los casos, pero de manera similar en el peor de los casos. Esta ganancia de rendimiento se debe principalmente al hecho de que el generador se evalúa de manera perezosa, lo que significa que solo calculará lo que se requiere para producir un valor.
Peor de los casos
# 10000000
# 5 function calls in 1.733 seconds
#
# Ordered by: standard name
#
# ncalls tottime percall cumtime percall filename:lineno(function)
# 2 1.455 0.727 1.455 0.727 so_lc.py:10(<genexpr>)
# 1 0.278 0.278 1.733 1.733 so_lc.py:9(<module>)
# 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
# 1 0.000 0.000 1.455 1.455 {next}
Mejor caso
best_case = [(True, 'T')] + ([(False, 'F')] * 10000000)
print next((i for i, v in enumerate(best_case) if v[0] == True), None)
# 0
# 5 function calls in 0.316 seconds
#
# Ordered by: standard name
#
# ncalls tottime percall cumtime percall filename:lineno(function)
# 1 0.316 0.316 0.316 0.316 so_lc.py:6(<module>)
# 2 0.000 0.000 0.000 0.000 so_lc.py:7(<genexpr>)
# 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
# 1 0.000 0.000 0.000 0.000 {next}
¡¿QUÉ?! El mejor caso estalla las comprensiones de la lista, pero no esperaba que nuestro peor caso superara las comprensiones de la lista hasta tal punto. ¿Como es eso? Francamente, solo podría especular sin más investigación.
Tome todo esto con un grano de sal, no he ejecutado ningún perfil sólido aquí, solo algunas pruebas muy básicas. Esto debería ser suficiente para apreciar que una expresión generadora es más eficaz para este tipo de búsqueda de listas.
Tenga en cuenta que todo esto es Python básico integrado. No necesitamos importar nada ni utilizar bibliotecas.
Vi por primera vez esta técnica de búsqueda en el curso Udacity cs212 con Peter Norvig.