Todos menos los últimos N elementos de iterador en Python

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

>>> 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] 

Solo 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 estaba especialmente preocupado por 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 

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

Basado en la descripción de Ignacio Vázquez-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 pregunté si era mejor usar el deque de derecha a izquierda (añadir, popleft) o de izquierda a derecha (appendleft, pop). Así que lo cronometré 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 añadir un solo elemento y hacer estallar un solo elemento.

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 lamentable que las collections no tengan un buffer circular. Esto sería más eficiente desde el punto de vista de la falta de caché con uno.

Para una lista puedes 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)) 

Se imprimirá:

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

Esto es compacto (aparte de consume receta tomada de itertools recetas de itertools ) y debe ejecutarse a velocidad C:

 import collections import operator from itertools import islice, tee def truncate(iterable, n): a, b = tee(iterable) consume(b, n) return map(operator.itemgetter(0), zip(a, b)) # From itertools recipes def consume(iterator, n=None): "Advance the iterator n-steps ahead. If n is None, consume entirely." # Use functions that consume iterators at C speed. if n is None: # feed the entire iterator into a zero-length deque collections.deque(iterator, maxlen=0) else: # advance to the empty slice starting at position n next(islice(iterator, n, n), None)