2011-07-09 24 views
5

¿Cuál es la mejor manera de obtener todos los N elementos de un iterador en Python? Aquí hay un ejemplo de ello en acción teórica:Todos menos los últimos N elementos del iterador en Python

>>> list(all_but_the_last_n(range(10), 0)) 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
>>> list(all_but_the_last_n(range(10), 2)) 
[0, 1, 2, 3, 4, 5, 6, 7] 
+0

que puedes usar 'collections.deque' como Ignacio recomienda. –

Respuesta

6

Por el gusto de hacerlo, aquí hay una variación de la solución de Ignacio que no requiere deque.

>>> def truncate(it, n): 
...  cache = [next(it) for i in range(n)] 
...  index = 0 
...  for val in it: 
...   val, cache[index] = cache[index], val 
...   index = (index + 1) % n 
...   yield val 

no era especialmente preocupado con la velocidad cuando escribí lo anterior ... pero quizás esto sería un poco más rápido:

def truncate(it, n): 
    cache = [next(it) for i in range(n)] 
    index = 0 
    for val in it: 
     yield cache[index] 
     cache[index] = val 
     index = (index + 1) % n 
+0

Buen uso de un buffer circular. Esta es probablemente la solución más rápida. –

+0

@Neil, gracias. No estoy seguro de por qué decidí usar 'while True' en retrospectiva ... allí, arreglado. – senderle

+0

Me gusta el buffer circular –

5

Utilice collections.deque. Presione N elementos desde la fuente en la primera invocación. En cada invocación posterior, saque un elemento, inserte un elemento desde la fuente y ceda el elemento reventado.

+0

No creo que eso fue lo que OP preguntó ... –

+0

@Fraklin: Por favor, siéntase libre de verificarlo dos veces. –

+0

+1: Es posible que desee señalar en caso de que no sea obvio que esta solución es O (N) memoria en lugar de O (M) donde M es la longitud de la secuencia. –

1

Usando la solución de Ignacio.

import collections 
def all_but_the_last_n(iterable, n): 
    it = iter(iterable) 
    fifo = collections.deque() 
    for _, i in zip(range(n), it): 
     fifo.append(i) 
    for i in it: 
     fifo.append(i) 
     yield fifo.popleft() 

print(list(all_but_the_last_n(range(10), 3))) 
print(list(all_but_the_last_n('abcdefghijkl', 3))) 

Es desafortunado que collections no tiene un buffer circular. Esto sería más eficiente desde el punto de vista de falta de caché con uno.

+1

ah iter (iterable) era lo que me estaba perdiendo. ¡Gracias! –

+0

en realidad, esto es incorrecto ... list (all_but_the_last_n (rango (10), 3)) -> [0, 1, 2, 4, 5, 6] en lugar de [0, 1, 2, ** 3 ** , 4, 5, 6] –

+0

@zdwiel: arreglado. –

3

Basándose en la descripción de Ignacio Vazquez-Abrams:

from collections import deque 

def all_but_the_last_n(iterable, count): 
    q = deque() 
    i = iter(iterable) 
    for n in range(count): 
     q.append(i.next()) 
    for item in i: 
     q.append(item) 
     yield q.popleft() 

Me preguntaba si era mejor utilizar el derecho deque a la izquierda (se añade, popleft) o de izquierda a derecha (appendleft, pop). Así que lo sincronicé con python 2.5.2 y encontré que rtl era 3.59 usec mientras que ltr era 3.53 usec. La diferencia de 0.06 usec no es significativa. la prueba consistió en agregar un solo elemento y abrir un solo elemento.

+0

bueno saber. Gracias por el tiempo! –

1

Para obtener una lista que podría hacer esto:

def all_but_the_last_n(aList, N): 
    return aList[:len(aList) - N] 

myList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
N = 4 

print(all_but_the_last_n(myList, N)) 

imprimirá:

[0, 1, 2, 3, 4, 5] 
Cuestiones relacionadas