2010-07-27 14 views
6

Estoy mirando a través de un archivo de texto para una cierta cadena con el método.python - tamaño del iterador invocable?

re.finditer(pattern,text) Me gustaría saber cuándo esto no devuelve nada. lo que significa que no pudo encontrar nada en el texto aprobado.

sé que se puede llamar, iteradores tienen next() y __iter__

me gustaría saber si podría obtener el tamaño o averiguar si devuelve ninguna cadena que coincide con mi patrón.

+3

Posible duplicado: http://stackoverflow.com/questions/3345785/getting-number-of-elements-in-an-iterator-in -python/ – Daenyth

+0

Si pega el código con el que está trabajando, podríamos encontrar mejores respuestas. –

Respuesta

5

Datos 3: La respuesta por @hynekcer es mucho, mucho mejor que esto.

EDIT 2: Esto no funcionará si tiene un iterador infinita, o uno que consume demasiadas Gigabytes (en 2010 1 gigabyte es todavía una gran cantidad de espacio de memoria RAM/disco) de espacio de memoria RAM/disco .

Ya has visto una buena respuesta, pero aquí hay un truco costoso que puedes usar si quieres comer un pastel y tenerlo también :) El truco es que tenemos que clonar el pastel, y cuando estás hecho de comer, lo colocamos de nuevo en la misma caja. Recuerde, cuando itera sobre el iterador, por lo general se vuelve vacío, o al menos pierde los valores devueltos anteriormente.

>>> def getIterLength(iterator): 
    temp = list(iterator) 
    result = len(temp) 
    iterator = iter(temp) 
    return result 

>>> 
>>> f = xrange(20) 
>>> f 
xrange(20) 
>>> 
>>> x = getIterLength(f) 
>>> x 
20 
>>> f 
xrange(20) 
>>> 

EDIT: Aquí es una versión más segura, pero utilizando todavía requiere cierta disciplina. No se siente bastante Pythonic. Obtendrá la mejor solución si publica la muestra completa del código relevante que está tratando de implementar.

>>> def getIterLenAndIter(iterator): 
    temp = list(iterator) 
    return len(temp), iter(temp) 

>>> f = iter([1,2,3,7,8,9]) 
>>> f 
<listiterator object at 0x02782890> 
>>> l, f = getIterLenAndIter(f) 
>>> 
>>> l 
6 
>>> f 
<listiterator object at 0x02782610> 
>>> 
+0

Esto no funciona con la mayoría de los iteradores o generadores. 'getIterLength' consumirá su' iterador'; asignando 'iter (temp)' a 'iterator' dentro de la función solo crea una nueva variable local llamada' iterator' allí que se descarta al regresar de la función. Intenta sustituir la línea 'f = xrange (20)' en tu ejemplo con 'f = iter ([1,2,3,4,5])' para ver a qué me refiero. –

+0

O compare 'id (f)' con 'id (iterador)' al inicio de la función (son lo mismo), 'id (iterador)' al final de la función (es diferente) y 'id (f) 'al regresar de la función (es lo mismo que antes). No está colocando el pastel clonado en la misma caja, lo está colocando en uno nuevo y tirándolo. –

+0

Es interesante, sin embargo, que sí funciona con 'xrange()'. Definitivamente no funciona con 're.finditer()'. –

5

Los iteradores de lo siento no están destinados a saber la longitud, solo saben lo que viene, lo que los hace muy eficientes al pasar por Colecciones. Aunque son más rápidos, no permiten la indexación, que incluye conocer la longitud de una colección.

+1

+1. Los iteradores no serían 1/5 tan útiles como lo son si se clavaran con cierta longitud de anticipación. Use (cualquier colección) para eso. – delnan

+0

no hay forma de saber la longitud a menos que itere a través de toda la secuencia. Los iteradores –

+0

son solo por eficiencia y, en general, deberían usarse si necesita pasar por una colección completa independientemente del orden, siempre es más rápido iterar a través de una matriz o colección con un iterador que incrementar un índice y verificar cada índice. –

1

se puede obtener el número de elementos en un iterador haciendo:

len([m for m in re.finditer(pattern, text) ]) 

iteradores son iteradores porque no han generado la secuencia todavía. Este código anterior básicamente está extrayendo cada elemento del iterador hasta que quiere detenerse en una lista, y luego toma la longitud de esa matriz. Algo que sería más eficiente de la memoria sería:

count = 0 
for item in re.finditer(pattern, text): 
    count += 1 

Un enfoque complicado al bucle para es el uso de reducir a contar correctamente los elementos del iterador uno por uno. Esto es efectivamente lo mismo que el bucle for:

reduce((lambda x, y : x + 1), myiterator, 0) 

Esto, básicamente, ignora la y pasado a reducir y sólo se suma uno. Inicializa la suma corriente en 0.

0

Una solución rápida sería convertir su iterador en una lista y verificar la longitud de esa lista, pero hacerlo puede ser malo para la memoria si hay demasiados resultados.

matches = list(re.finditer(pattern,text)) 
if matches: 
    do_something() 
print("Found",len(matches),"matches") 
10

Aquí es una solución que utiliza menos memoria, porque no guarda los resultados intermedios, al igual que las otras soluciones que utilizan "lista":

print sum(1 for _ in re.finditer(pattern, text)) 

Todas las otras soluciones tienen la desventaja de consumir mucha memoria si el patrón es muy frecuente en el texto, como el patrón '[az]'.

caso de prueba:

pattern = 'a' 
text = 10240000 * 'a' 

esta solución con sum(1 for ...) utiliza aproximadamente sólo la memoria para el texto como tal, que es len(text) bytes. Las soluciones anteriores con list pueden usar aproximadamente 58 o 110 veces más memoria de la necesaria. Es 580 MB para resp 32 bits. 1.1 GB para Python de 64 bits 2.7.

+0

¡Esto se ve bien! –

1

Si bien algunos iteradores podrían conocer su longitud (por ejemplo, se crearon a partir de una cadena o una lista) la mayoría no lo hace y no puede. re.iter es un buen ejemplo de uno que no puede saber su longitud hasta que se termine.

Sin embargo, hay un par de diferentes maneras de mejorar su código actual:

  • uso re.search para encontrar si hay coincidencias, a continuación, utilizar re.finditer para hacer el procesamiento real; o

  • utilizan un valor centinela con el for bucle.

La segunda opción se ve algo como:

match = empty = object() 
for match in re.finditer(...): 
    # do some stuff 
if match is empty: 
    # there were no matches 
Cuestiones relacionadas