views:

460

answers:

7

What's the best way of getting the last item from an iterator in Python 2.6? For example, say

my_iter = iter(range(5))

What is the shortest-code / cleanest way of getting 4 from my_iter?

I could do this, but it doesn't seem very efficient:

[x for x in my_iter][-1]
+10  A: 
item = defaultvalue
for item in my_iter:
    pass
Thomas Wouters
Why the placeholder "defaultvalue"? Why not `None`? This is precisely what `None` is for. Are you suggesting that some function-specific default value could even be correct? If the iterator doesn't actually iterate, then an out-of-band value is *more* meaningful than some misleading function-specific default.
S.Lott
The defaultvalue is just a placeholder for my example. If you want to use `None` as the default value, that's your choice. None is not always the most sensible default, and may not even be out of band. Personally I tend to use 'defaultvalue = object()' to make sure it's a truly unique value. I'm just indicating that the choice of default is outside of the scope of this example.
Thomas Wouters
@S.Lott: perhaps it is useful to distinguish the difference between an empty iterator and an iterator that has `None` as it's final value
gnibbler
If your iterator can have `None` as a legitimate value, you've probably designed it wrong. An exception may have made more sense than a `None`. Particularly when this use case exists as part of the design.
S.Lott
There's a design error in all iterators of all builtin container types? First time I've heard of it :)
Thomas Wouters
While this is probably the faster solution, it relies on the variable leaking in for-loops (a feature for some, a bug for others - probably FP-guys are appalled). Anyhow, Guido said this will always work this way, so it's safe construction to use.
tokland
This is a faster solution, it is not the fastest. The fastest is to do the for loop in C, which is done by using `collections.deque` as per martin23487234's late answer. Similar to how `consume` in the [itertools recipes](http://docs.python.org/library/itertools.html#recipes) works.
Muhammad Alkarouri
+1  A: 

I would use reversed, except that it only takes sequences instead of iterators, which seems rather arbitrary.

Any way you do it, you'll have to run through the entire iterator. At maximum efficiency, if you don't need the iterator ever again, you could just trash all the values:

for last in my_iter:
    pass
# last is now the last item

I think this is a sub-optimal solution, though.

Chris Lutz
reversed() doesn't take an iterator, just sequences.
Thomas Wouters
It's not at all arbitrary. The only way to reverse an iterator is to iterate through to the end, while keeping all the items in memory. I, e, you need to first make a sequence out of it, before you can reverse it. Which of course defeats the purpose of the iterator in the first place, and also would mean you suddenly use up a lot of memory for no apparent reason.So it's the opposite of arbitrary, in fact. :)
Lennart Regebro
@Lennart - When I said arbitrary, I meant annoying. I'm focusing my language skills on my paper due in a few hours at this time in the morning.
Chris Lutz
Fair enough. Although IMO it would be more annoying if it did accept iterators, because almost any use of it would be a Bad Idea (tm). :)
Lennart Regebro
A: 

There's this

list( the_iter )[-1]

If the length of the iteration is truly epic -- so long that materializing the list will exhaust memory -- then you really need to rethink the design.

S.Lott
+5  A: 

This is unlikely to be faster than the empty for loop due to the lambda, but maybe it will give someone else an idea

reduce(lambda x,y:y,my_iter)

If the iter is empty, a TypeError is raised

gnibbler
+1 yeah, it probably won't be the faster, but when in doubt, use functional constructions.
tokland
+5  A: 

Probably worth using __reversed__ if it is available

if hasattr(my_iter,'__reversed__'):
    last = next(reversed(my_iter))
else:
    for last in my_iter:
        pass
gnibbler
A: 

The question is wrong and can only lead to an answer that is complicated and inefficient. To get an iterator, you of course start out from something that is iterable, which will in most cases offer a more direct way of accessing the last element.

Once you create an iterator from an iterable you are stuck in going through the elements, because that is the only thing an iterable provides.

So, the most efficient and clear way is not to create the iterator in the first place but to use the native access methods of the iterable.

ludwig
+1  A: 

Use a deque of size 1.

from collections import deque

#aa is an interator
aa = iter('apple')

dd = deque(aa, maxlen=1)
last_element = dd.pop()
martin23487234