161

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]
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Peter
  • 127,331
  • 53
  • 180
  • 211
  • 5
    Iterators assume that you want to iterate through the elements and not access last elements really. What stops you from simply using range(5)[-1] ? – Frank Jan 26 '10 at 10:57
  • 7
    @Frank - I assumed the actual iterator was more complex and/or further away and/or harder to control than `iter(range(5))` – Chris Lutz Jan 26 '10 at 10:58
  • 3
    @Frank: the fact that it's actually a much more complicated generator function that supplies the iterator. I just made this example up so that it was simple and clear what was happening. – Peter Jan 26 '10 at 10:59
  • 4
    If you want the last item of an iterator, there is a big chance you are doing something wrong. But the answer is that there isn't really any cleaner way that iterating through the iterator. This is because iterators don't have a size, and in fact, may not ever end at all, and as such may not have a last item. (Meaning your code will run forever, of course). So the lingering question is: Why do you want the last item of an iterator? – Lennart Regebro Jan 26 '10 at 11:17
  • @Lennart: ok, I hoped to avoid these issues for clarity, but here goes: I have a function that operates in two modes. In one, I get intermediate results, and report them back. In another ('batch mode') I don't care about the intermediate reporting, and just want the last result. I can modify the function, but performance doesn't especially matter, so I'll just chuck away the intermediate results and keep the last. – Peter Jan 26 '10 at 11:22
  • 4
    @Peter: Update your question, please. Do not add a bunch of comments to a question you own. Please update the question and remove the comments. – S.Lott Jan 26 '10 at 11:40
  • 1
    @Peter: Avoiding the use case doesn't increase the clarity. In fact it decreases it. :) – Lennart Regebro Jan 26 '10 at 13:45
  • @Peter: I think many good solutions have been given. However, if this is coming from a class' _ _ iter _ _ method, you should consider implementing _ _ reversed _ _ as well (if possible), which would make things a little cleaner. – tixxit Jan 26 '10 at 15:08

14 Answers14

152

If you are using Python 3.x:

*_, last = iterator # for a better understanding check PEP 448
print(last)

if you are using python 2.7:

last = next(iterator)
for last in iterator:
    continue
print last


Side Note:

Usually, the solution presented above is what you need for regular cases, but if you are dealing with a big amount of data, it's more efficient to use a deque of size 1. (source)

from collections import deque

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

dd = deque(aa, maxlen=1)
last_element = dd.pop()
Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
Dhia
  • 10,119
  • 11
  • 58
  • 69
  • Could you please explain the `*_` part? – virtualxtc Jun 28 '18 at 00:26
  • @virtualxtc check [PEP 448](https://www.python.org/dev/peps/pep-0448/) for more details – Dhia Jun 28 '18 at 09:49
  • 2
    @virtualxtc: The underscore is just an identifier. The star in front says "expand the list". The more readable would be `*lst, last = some_iterable`. – pepr Jun 28 '18 at 09:57
  • 5
    @virtualxtc nope `_` is special variable in python and used either to store the last value or to say i don't care about the value so could be cleaned. – Dhia Jun 28 '18 at 10:02
  • 13
    That Python 3 solution is not memory-efficient. – Markus Strauss Sep 03 '18 at 11:26
  • @MarkusStrauss I mean it depends on the size and the level of optimisation needed, for regular use cases should be more than enough, but i would be happy to know your way of doing it :) – Dhia Sep 03 '18 at 12:30
  • 4
    @DhiaTN Yes, you are absolutely right. Actually, I like the Python 3 idiom you showed a lot. I just wanted to make clear, that it does not work for "big data". I use collections.deque for that, which happens to be fast and memory efficient (see solution from martin23487234). – Markus Strauss Sep 06 '18 at 10:56
  • 1
    This py3.5+ example should be in PEP 448. Wonderful. – EliadL Jan 10 '19 at 09:42
  • 13
    `*_, last = iterator` will raise if `iterator` is empty. Be prepared to `try`/`except` if you're using it on data which may be empty. – Nic Jul 16 '19 at 21:50
  • What does "NB" mean in this context? Don't think I've seen it in my 30+ years on the internet. – Catskul Jan 23 '20 at 17:28
  • I means side note – Dhia Jan 24 '20 at 13:22
  • 10
    @DhiaTN `_` isn't a special variable in Python. It's a normal identifier that acts like any other. You're right that it's **conventionally** used to say "I don't care about this value" because it looks unusual for a varname, but that's just convention; Python itself doesn't treat it specially at all, unlike in languages like Go where `_` is reserved by the language for a throwaway identifier and is unable to store values. (The Python REPL's use of `_` to store the last value is also unrelated to the Python language itself, it's just another example of convention) –  Jul 01 '20 at 05:04
  • 7
    That's important to understand because it explains Markus Strauss's assertion that the 3.x example is memory-inefficient. See, if `_` were actually a spec-mandated throwaway identifier, then `*_, last = some_huge_iterator()` would run totally fine no matter how big the iterator were. The throwaway values would be discarded immediately upon generation and they'd never see the light of day. But because `_` is just a normal identifier, all that snippet does is construct a big old list of values and store it all in the variable named `_`. That's why it ends up using a lot of memory on "big data". –  Jul 01 '20 at 05:09
  • 1
    Note: `_` *is* special in one way; it's always the value of the last expression in the REPL interactive environment. Assigning to it explicitly destroys that specialness. But yeah, as user4698348 notes, it's not special in any other way; `_` will be an arbitrarily huge `list`. The `deque` solution is the fastest way, the "Python 2.7" solution (which could just use `pass`, `continue` implies skipping while `pass` is explicitly "do nothing") works perfectly well in all versions of Python and doesn't involve storing the whole input just to get the last element from it. – ShadowRanger Sep 29 '22 at 20:05
  • 1
    The "I don't care" meaning of `_` is purely by convention; *no* version of Python would actually be *allowed* to preemptively discard it, because `_` is a perfectly legal name, and accessing it later is legal. – ShadowRanger Sep 29 '22 at 20:06
132
item = defaultvalue
for item in my_iter:
    pass
Thomas Wouters
  • 130,178
  • 23
  • 148
  • 122
  • 5
    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 Jan 26 '10 at 11:44
  • 63
    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 Jan 26 '10 at 11:51
  • 34
    @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 – John La Rooy Jan 26 '10 at 11:54
  • 1
    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 Jan 26 '10 at 16:39
  • 10
    There's a design error in all iterators of all builtin container types? First time I've heard of it :) – Thomas Wouters Jan 26 '10 at 20:55
  • 7
    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 Jul 23 '10 at 19:08
  • 2
    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 Sep 15 '10 at 12:55
  • `item` does not need to be defined first; rather than using `item = defaultvalue` I suggest just iterating and capturing the `NameError` for the case of an empty iterator – Chris_Rands May 29 '17 at 13:12
  • 3
    Capturing NameError is not a good idea, since you can't be sure *which* NameError you catch. You can end up hiding bugs in the iterator itself. – Thomas Wouters Jun 12 '17 at 13:16
  • 1
    @tokland There is no variable leaking. Loops in Python simply do not create a new scope. More details: [Scoping in Python 'for' loops](https://stackoverflow.com/q/3611760/320437) – pabouk - Ukraine stay strong Jun 15 '22 at 05:32
85

Use a deque of size 1.

from collections import deque

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

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

Short version:

last_element, = deque(aa, 1)
Kelly Bundy
  • 23,480
  • 7
  • 29
  • 65
martin23487234
  • 851
  • 6
  • 2
  • 8
    This is actually the fastest way to exhaust a long sequence, though only slighlty faster than the for loop. – Sven Marnach Jan 22 '11 at 02:21
  • 13
    +1 for being technically correct, but readers should have the usual Python caveats of, "Do you REALLY need to optimize this?", "This is less explicit, which is not Pythonic", and "The faster speed depends on implementation, which may change." – leewz Feb 17 '14 at 20:50
  • 1
    also, it is a memory-hog – Eelco Hoogendoorn Jul 28 '16 at 10:54
  • 11
    @EelcoHoogendoorn Why is it a memory-hog, even with a maxlen of 1? – Chris Wesseling Dec 15 '16 at 13:20
  • 4
    From all solutions presented here so far, I find this to be the fastest and _most memory-efficient_ one. – Markus Strauss Sep 03 '18 at 11:24
  • 1
    @EelcoHoogendoorn no, it is O(1) space – juanpa.arrivillaga Sep 29 '22 at 19:41
  • To be clear, there is no need to explicitly convert to an iterator with `iter` first, and you don't need to use `pop`; `last_element = deque('apple', maxlen=1)[0]` or `last_element, = deque('apple', maxlen=1)` both work just fine (the latter is just [a cheap trick for cheaply extracting exactly one item from a one element iterable](https://stackoverflow.com/a/33161467/364696)). – ShadowRanger Sep 29 '22 at 20:09
  • @ShadowRanger Well, the question is about iterators, so I'd say it's proper to use a demo setup creating an iterator, and the unpacking solution is already in the answer. – Kelly Bundy Sep 29 '22 at 21:11
40

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
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • Hello kind sir, Is this okay to use when dealing with big amount of data? – Miko Chu Jan 27 '23 at 16:06
  • 1
    @MikoChu, Python types such as list, dict, OrderedDict have __reversed__ defined. This saves iterating the whole collection to find the last item. You are free to implement a __reversed__ method on your own collection classes if there is an efficient way to iterate from the opposite end. – John La Rooy Feb 01 '23 at 00:39
38

As simple as:

max(enumerate(the_iter))[1]
Chema Cortes
  • 389
  • 3
  • 2
  • 11
    Oh, this is clever. Not the most efficient or readable, but clever. – timgeb Mar 26 '16 at 17:56
  • 11
    So just thinking out loud... This works because `enumerate` returns `(index, value)` like: `(0, val0), (1, val1), (2, val2)`... and then by default `max` when given a list of tuples, compares against only the first value of the tuple, unless two first values are equal, which they never are here because they represent indices. Then the trailing subscript is because max returns the entire (idx, value) tuple whereas we're only interested in `value`. Interesting idea. – Taylor D. Edmiston Jun 12 '17 at 02:18
24

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

John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • IMHO, this one is the most direct, conceptually. Instead of raising `TypeError` for an empty iterable, you could also supply a default value via the initial value of `reduce()`, e.g., `last = lambda iterable, default=None: reduce(lambda _, x: x, iterable, default)`. – egnha Dec 11 '19 at 08:25
13

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
  • 384,516
  • 81
  • 508
  • 779
  • 3
    This is the most straightforward solution. – laike9m Mar 23 '14 at 07:41
  • 2
    Mildly better to use a tuple. – Christopher Smith Feb 09 '15 at 20:10
  • 17
    Strongly disagree with the last sentence. Working with very large datasets (that could exceed memory bounds if loaded all at once) is the main reason to use an iterator instead of a list. – Paul Apr 07 '16 at 16:41
  • 1
    @Paul: some functions only return an iterator. This is a short and pretty readable way to do it in that case (for non-epic lists). – serv-inc Nov 14 '17 at 09:24
  • 1
    That's the least efficient way one should avoid as bad bad bad habit. Another one is to use sort(sequence)[-1] to get the max element of the sequence. Please never use these ill paterns if you like to be software engineer. – Maksym Ganenko Aug 28 '19 at 13:35
  • This is pretty much what the OP said he didn't want to do. – Jim Sep 29 '20 at 02:55
6

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
  • 73,191
  • 16
  • 130
  • 183
  • 4
    reversed() doesn't take an iterator, just sequences. – Thomas Wouters Jan 26 '10 at 11:02
  • 6
    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 Jan 26 '10 at 11:15
  • @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 Jan 26 '10 at 11:24
  • 3
    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 Jan 26 '10 at 14:12
  • `reversed()` can operate on iterators supporting it. Since Python 2.6 from 2008. – Robert Siemer Jan 08 '23 at 10:23
4

The toolz library provides a nice solution:

from toolz.itertoolz import last
last(values)

But adding a non-core dependency might not be worth it for using it only in this case.

lumbric
  • 7,644
  • 7
  • 42
  • 53
  • The "nice solution" is to try `seq[-1:]` and if this fails to use `collections.deque(seq, 1)`. `toolz` are pure Python. `toolz.last(seq)` uses `toolz.tail(1, seq)` and `toolz.tail(1,seq)` tries to get a value of `seq[-1:]` and if this fails it gives `collections.deque(seq, 1)`. – Claudio Mar 30 '22 at 19:50
0

See this code for something similar:

http://excamera.com/sphinx/article-islast.html

you might use it to pick up the last item with:

[(last, e) for (last, e) in islast(the_iter) if last]
  • 3
    Please include the code for `islast` in your answer (see http://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-answers). – Cristian Ciupitu Apr 23 '15 at 15:34
0

The question is about getting the last element of an iterator, but if your iterator is created by applying conditions to a sequence, then reversed can be used to find the "first" of a reversed sequence, only looking at the needed elements, by applying reverse to the sequence itself.

A contrived example,

>>> seq = list(range(10))
>>> last_even = next(_ for _ in reversed(seq) if _ % 2 == 0)
>>> last_even
8
Wyrmwood
  • 3,340
  • 29
  • 33
0

You could use the great more_itertools library and the last(iterable[, default]) function:

more_itertools.last(iterable[, default]) [source]

Return the last item of iterable, or default if iterable is empty.

>>> last([0, 1, 2, 3])
3
>>> last([], 'some default')
'some default'

If default is not provided and there are no items in the iterable, raise ValueError.

It combines the deque approach proposed by some of the answers here, with some additional improvements (see the source for details).

Czechnology
  • 14,832
  • 10
  • 62
  • 88
-1

I would just use next(reversed(myiter))

serv-inc
  • 35,772
  • 9
  • 166
  • 188
thomas.mac
  • 1,236
  • 3
  • 17
  • 37
-9

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
  • 39
  • 2
  • 7
    So how would you get the last line of a file? – Brice M. Dempsey Jul 25 '15 at 05:20
  • @BriceM.Dempsey The best way is not by iterating through the whole (maybe huge) file but by going to the file size minus 100, read the last 100 bytes, scan for a newline in them, if there isn't any, go back another 100 bytes, etc. You can also increase your step-back size, depending on your scenario. Reading a gazillion lines definitely is a non-optimal solution. – Alfe Jun 04 '19 at 12:15
  • @BriceM.Dempsey: [`mmap` allows this in clever ways, without even needing to do stepped back reads as Alfe suggests](https://stackoverflow.com/a/34029605/364696). This is a bad answer to the problem as given (sometimes you've just got a plain iterator and no ability to change it, and a solution is needed), but yeah, when you can, you want to avoid having the problem in the first place. – ShadowRanger Sep 29 '22 at 20:12