2010-04-30 52 views
16

El tipo de lista de Python tiene un método de índice (x). Toma un solo parámetro x y devuelve el índice (entero) del primer elemento en la lista que tiene el valor x.En Python, ¿cómo puedo encontrar el índice del primer elemento en una lista que NO tiene algún valor?

Básicamente, necesito invertir el método de índice (x). Necesito obtener el índice del primer valor en una lista que NO tiene el valor x. ¡Probablemente podría incluso usar una función que devuelva el índice del primer artículo con un valor! = Ninguno.

Puedo pensar en una implementación de bucle 'para' con una variable de incremento de contador, pero siento que me falta algo. ¿Existe un método existente o una construcción de Python de una línea que pueda manejar esto?

En mi programa, aparece la situación cuando manejo listas devueltas de coincidencias de expresiones regulares complejas. Todos los elementos, menos uno, en cada lista tienen un valor de Ninguno. Si solo necesitaba la cadena coincidente, podría usar una comprensión de lista como '[x para x en [mi_lista] si x no es Ninguno]', pero necesito el índice para averiguar qué grupo de captura en mi expresión real causó realmente el partido.

+0

¿Está seguro de que necesita el índice y no el valor real? –

+0

Una comprensión de lista es básicamente lo mismo que un bucle for, pero escrito de una manera diferente (que a menudo puede ser más difícil de leer).Recomiendo simplemente usar un bucle for independientemente (con enumerar) – mathmike

Respuesta

18

salir en el primer partido es muy fácil: en lugar de la informática una lista completa de comprensión (luego tirando todo menos el primer elemento), use next sobre un genexp. Suponiendo, por ejemplo, que desea -1 cuando no hay ningún elemento satisface la condición de ser != x,

return next((i for i, v in enumerate(L) if v != x), -1) 

Esta es la sintaxis de Python 2.6; si está atrapado con 2.5 o anterior, .next() es un método del genexp (u otro iterador) y no acepta un valor predeterminado como el -1 anterior (por lo tanto, si no desea ver una excepción StopIteration, tiene que usar un try/except). Pero entonces, allí es una razón por la que se hicieron más lanzamientos después de 2.5 - ¡mejora continua del lenguaje y sus integradas! -)

+0

En realidad, no es necesario que maneje la condición "no se cumple el elemento", ya que el método de expresión regular siempre devuelve al menos un elemento de lista que no es Ninguno. Entonces creo que esto debería funcionar: el próximo (i para i, x en enumerate (my_list,) si x no es None). Debajo del depurador, parece detenerse en el primer partido, así que estoy vendido. Buen truco. –

+0

@Ryan, sí, si no necesita un resultado predeterminado cuando todos los elementos son Ninguno, su expresión más simple funcionará bien. –

4

enumerate() devuelve un iterador que produce una tupla del índice actual de la iterable, así como del elemento en sí.

+1

Aw, realmente me faltaba algo. Sí, el uso exacto es algo como esto: [i para i, x en enumerate (my_list) si x no es None]. ¡Gracias! –

1
[i for i, x in enumerate(my_list) if x != value][0] 

Si no está seguro de si hay un elemento no coincidente, utilizar esto en su lugar:

match = [i for i, x in enumerate(my_list) if x != value] 
if match: 
    i = match[0] 
    # i is your number. 

Usted puede hacer esto aún más "funcional" con itertools, pero pronto llegará a la punto donde un simple for loop es mejor. Incluso las soluciones anteriores no son tan eficientes como un bucle for, ya que construyen una lista de todos los índices que no coinciden antes de extraer el de interés.

5

Usar una lista de comprensión cuando solo necesitas la primera me parece viscosa (para mí). Use un for-loop y salga temprano.

>>> lst = [None, None, None, "foo", None] 
>>> for i, item in enumerate(lst): 
... if item: break 
... else: 
... print "not found" 
... 
>>> i 
3 
+0

¿Has leído el tercer párrafo en mi pregunta original? La implementación for loop es trivial. Estaba buscando específicamente un método de una línea o uno existente. (Basado en los votos, sin embargo, parece que usted no fue la única persona que no entendió el punto. Debería haberlo hecho un poco más obvio.) –

+0

@Ryan B. Lynch: Lo leí. Mencionaste que podrías hacerlo usando un for-loop y un contador ... que es más código y un poco feo. Usé enumerar(), para obtener el contador automáticamente. De su pregunta, no estaba seguro de si sabía que existía enumerate. OTOH, no sabía que next() existía (pero duró el uso de python 2.5), así que aprendí algo de la solución de Alex. No sé por qué la gente votó a favor de esto, tal vez los votos vinieron de tratar de hacer algo en una línea que se puede hacer trivial y transparentemente en dos? – Stephen

+0

Buen punto sobre el contador contra romper algo. En cuanto a las opiniones sobre "tratar de hacer algo en una sola línea que se pueda hacer trivial y transparentemente en dos", la respuesta original lo transmitió con bastante claridad. Pero si las consideraciones de estilo de codificación motivan su respuesta, ¿por qué convertirla en una respuesta? ¿No son para eso los comentarios? –

0

Una solución tonta basada en itertools :)

import itertools as it, operator as op, functools as ft 

def index_ne(item, sequence): 
    sequence= iter(sequence) 
    counter= it.count(-1) # start counting at -1 
    pairs= it.izip(sequence, counter) # pair them 
    get_1st= it.imap(op.itemgetter(0), pairs) # drop the used counter value 
    ne_scanner= it.ifilter(ft.partial(op.ne, item), get_1st) # get only not-equals 
    try: 
     ne_scanner.next() # this should be the first not equal 
    except StopIteration: 
     return None # or raise some exception, all items equal to item 
    else: 
     return counter.next() # should be the index of the not-equal item 

if __name__ == "__main__": 
    import random 

    test_data= [0]*20 
    print "failure", index_ne(0, test_data) 

    index= random.randrange(len(test_data)) 
    test_data[index]= 1 
    print "success:", index_ne(0, test_data), "should be", index 

Todo esto sólo para tomar ventaja de la itertools.count conteo :)

+0

Tenga en cuenta que, en algunas ocasiones, se podría usar 'item .__ ne__' en lugar del dispositivo' ft.partial (op.ne, item) 'anterior; sin embargo, este último funciona con todos los 'item's. – tzot

+4

Esto es impresionantemente incomprensible. –

Cuestiones relacionadas