Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does python itertools recipe consume() mean at all?

I was learning the itertools through python docs:

https://docs.python.org/3/library/itertools.html

And at the very end there are recipes that use itertools commands to do simple things, one of them, the consume(), I don't understand it at all:

from itertools import *
def consume(iterator, n):
    """"Advance the iterator n-steps ahead. If n is none, consume 
        entirely."""
    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)

first of all, this function claims argument to be an iterator, but I found that an iterable will do as well. I think there is a difference between iterable and iterator, right? Because islice() needs no iterator, an iterable could be used

Secondly, When I tried it:

aa = iter([1,2,3,4,5])
print(consume(aa, 2))

it give me None, because islice(iterator, n, n) will always be None no matter what, as (n, n) has no range to be sliced.

And of course if n is None, then I will definitely get None So it seems like no matter what I do here, I get None as the output anyway, what is the purpose of this function at all?

like image 853
jxie0755 Avatar asked May 23 '26 21:05

jxie0755


2 Answers

consume is not supposed to return anything useful. Like the docs says, its purpose is to advance the existing iterator. If you look at aa after your example, you will see it has been advanced:

>>> aa = iter([1,2,3,4,5])
... print(consume(aa, 2))
None
>>> next(aa)
3

Because the use of consume is in its side effect, you can use it on an a "re-iterable" object, but doing so will be useless. What happens is that islice will create an iterator over the object, and advance that iterator, but that won't affect subsequent iteration over the iterable, because a new iterator will be created:

# consuming an iterator
aa = iter([1,2,3,4,5])
consume(aa, 2)
print(list(aa))
# [3, 4, 5]

# "consuming" an iterable
aa = [1,2,3,4,5]
consume(aa, 2)
print(list(aa))
# [1, 2, 3, 4, 5]

In the latter case, all you consumed was a temporary iterator that was created inside consume itself but not returned, so it had no observable effect.

(I use the term "re-iterable" to refer to iterable objects which generate a "fresh" iterator over some stable base data each time iter is called. Lists, for instance, are re-iterables. Every re-iterable object is iterable, but you can write an iterable object which is not re-iterable.)

like image 129
BrenBarn Avatar answered May 26 '26 09:05

BrenBarn


Iterating through a list is kind of pointless unless you do something with its elements, but advancing through an iterator can execute code; for example, you might be iterating through the values returned by a function with side effects. consume() walks through several elements and throws them away. The general idea (if you don't consume everything) is that it works like seek() in a file handle: When you try to get something out of the iterator afterwards, you will be at a different position.

like image 39
alexis Avatar answered May 26 '26 11:05

alexis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!