tutorial lenguaje descargar python

lenguaje - python tutorial



Índice de Python del elemento en la lista sin error? (6)

Posible duplicado:
Python list.index pregunta

Para encontrar el índice de un elemento en una lista, utiliza:

list.index(x) Return the index in the list of the first item whose value is x. It is an error if there is no such item.

Eso me parece un poco extraño, que arrojaría un error si no se encontrara el elemento. De donde provengo (tierra de Objective-C), devuelve una enumeración NSNotFound (que es solo un int máximo, lo que indica que no se encontró el elemento).

Así que hice algo feo para rodear esto:

index = 0 for item in self.items: if item.id == desired_id: return index index = index + 1 return -1

Usé -1 para indicar que el artículo no fue encontrado. ¿Cuál es la mejor manera de hacer esto y por qué Python no tiene algo como esto incorporado?


Es mejor pensar que es "generar una excepción" que "lanzar un error".

Las excepciones en Python no son solo por errores, son por circunstancias excepcionales , de ahí el nombre. Si list.index() hubiera devuelto algún valor especial, tendría que ser uno que

  1. no podría haber sido devuelto si list.index() hubiera encontrado el artículo

  2. Posteriormente no pudo ser malinterpretado por código ingenuo.

La primera condición excluye todos los enteros positivos (incluidos cero y sys.maxint ), y la segunda también excluye los negativos (porque los índices negativos son una forma válida de indexar en una lista en Python). Cualquier otra cosa que no sea un número entero es probable que genere una excepción más tarde, de todos modos, si el código subsiguiente asume que eso es lo que obtendrá.

Independientemente de si el método genera una excepción o devuelve un valor especial, a menudo tendrá que hacer algo con esa información, y esto:

try: index = list.index(x) except ValueError: # do something

es más legible que esto:

index = list.index(x) if index == some_special_value: # do something

... y en este último caso, olvidarse de protegerse contra las circunstancias excepcionales causaría que el código falle en silencio, probablemente llevando a errores confusos en otras partes del código.

Peor aún, tendrías que recordar o buscar cuál es ese valor especial, para este y cualquier otro método o función que se comporte de esa manera.


Estoy de acuerdo con la solución general que se señaló, pero me gustaría examinar un poco más los enfoques que se explicaron en las respuestas y los comentarios para ver cuál es más eficiente y en qué situaciones.

En primer lugar, los tres enfoques básicos:

>>> def my_index(L, obj): ... for i, el in enumerate(L): ... if el == obj: ... return i ... return -1 ... >>> def my_index2(L, obj): ... try: ... return L.index(obj) ... except ValueError: ... return -1 ... >>> def my_index3(L, obj): ... if obj in L: ... return L.index(obj) ... return -1 ...

La primera y la segunda solución escanean la lista solo una vez, por lo que puede pensar que son más rápidas que la tercera porque escanea la lista dos veces. Así que veamos:

>>> timeit.timeit(''my_index(L, 24999)'', ''from __main__ import my_index, L'', number=1000) 1.6892211437225342 >>> timeit.timeit(''my_index2(L, 24999)'', ''from __main__ import my_index2, L'', number=1000) 0.403195858001709 >>> timeit.timeit(''my_index3(L, 24999)'', ''from __main__ import my_index3, L'', number=1000) 0.7741198539733887

Bueno, el segundo es realmente el más rápido, pero puedes notar que el primero es mucho más lento que el tercero, aunque escanea la lista solo una vez. Si aumentamos el tamaño de la lista las cosas no cambian mucho:

>>> L = list(range(2500000)) >>> timeit.timeit(''my_index(L, 2499999)'', ''from __main__ import my_index, L'', number=100) 17.323430061340332 >>> timeit.timeit(''my_index2(L, 2499999)'', ''from __main__ import my_index2, L'', number=100) 4.213982820510864 >>> timeit.timeit(''my_index3(L, 2499999)'', ''from __main__ import my_index3, L'', number=100) 8.406487941741943

El primero es todavía 2 veces más lento.

y si buscamos algo que no está en la lista, las cosas empeoran aún más con la primera solución:

>>> timeit.timeit(''my_index(L, None)'', ''from __main__ import my_index, L'', number=100) 19.055058002471924 >>> timeit.timeit(''my_index2(L, None)'', ''from __main__ import my_index2, L'', number=100) 5.785136938095093 >>> timeit.timeit(''my_index3(L, None)'', ''from __main__ import my_index3, L'', number=100) 5.46164608001709

Como se puede ver en este caso, la tercera solución supera incluso a la segunda, y ambas son casi 4 veces más rápidas que el código de Python. Dependiendo de la frecuencia con la que espere que la búsqueda falle, desea elegir el # 2 o el # 3 (aunque en el 99% de los casos, el número # 2 es mejor).

Como regla general, si desea optimizar algo para CPython, entonces desea hacer tantas iteraciones "a nivel C" como pueda. En su ejemplo, la iteración usando un bucle for es exactamente algo que no quiere hacer.


Hay una razón clara para este comportamiento:

>>> from import this ... In the face of ambiguity, refuse the temptation to guess. ...

No hay una interpretación clara sobre cómo el sistema debe responder a un objeto como "NSNotFound", por lo que debe negarse a adivinar, y luego se volvió inútil implementar una función especial para eso.

Piensa qué pasa si intento hacer algo como esto:

[ objective.index(i)+1 for i in reference_list ]

¿Qué significa agregar 1 al NSNotFound? ¿No es más simple hacer algo como:

[ objective.index(i)+1 for i in reference_list if i in objective ]

Y (-1) es en realidad un índice válido para una lista, significa "toma el último valor", por lo que si intentas usarlo como un código de error especial, es muy probable que vayas a terminar en algo desagradable. bicho desagradable.

Guido tiene un gran sentido del diseño, no lo subestimes;)

Si, dicho esto, todavía necesitas algo así, puedes probar con este código:

class NotFoundError(Exception): def __init__(self,container,index): self.message = "object "+str(index)+" not found on "+str(container) self.container = container self.index = index def __str__(self): return self.message def getindex(cont,idx): try: return cont.index(idx) except: return NotFoundError(cont,idx) a = [1,2] print getindex(a,3) #object 3 not found on [1, 2]



use la gestión de excepciones, list.index genera ValueError para que pueda capturar esa excepción:

Un ejemplo simple:

In [78]: lis=[1,2,3,4] In [79]: for i in range(-1,6): try: print lis.index(i) except ValueError: print i,"not found" -1 not found 0 not found 0 1 2 3 5 not found


a = [1] try: index_value = a.index(44) except ValueError: index_value = -1

¿Qué tal esto?