Reputation: 1201
I'm looking for a pythonic way to iterate through a list and do something on the last (and only the last) element. There are two ways I can see to do this, of which I would guess the second is best:
for item in a_list:
#do something to every element
if a_list.index(item) == len(a_list) - 1:
# do something to the last one
and
for n, item in enumerate(a_list):
#do something to every element
if n == len(a_list) - 1 :
# do something to the last one
However, I wonder if there is a way of doing it without calling len()
on a list I'm already iterating over. I'm quite happy, by the way, to be told that this isn't something I should worry about.
Upvotes: 0
Views: 8522
Reputation: 103844
Consider:
li[:]=[do_somthing(item) for item in li] # something to every item in place
li[-1]=something_extra(li[-1]) # additional to last item
vs
for i, item in enumerate(li):
li[i]=do_somthing(item)
if i==len(li)-1:
li[i]=something_extra(item)
If you time these, you can see this is the fastest way:
def do_something(e):
return e*2
def something_extra(e):
return e/2
def f1(li):
for i, item in enumerate(li):
li[i]=do_something(item)
if i==len(li)-1:
li[i]=something_extra(item)
def f2(li):
li[:]=[do_something(item) for item in li]
li[-1]=something_extra(li[-1])
def f3(li):
for i, item in enumerate(li):
li[i]=do_something(item)
li[i]=something_extra(item)
if __name__ == '__main__':
import timeit
for f in (f1,f2,f3):
t=timeit.timeit("f(range(1000))",
setup="from __main__ import f,do_something,something_extra",
number=10000)
print '{}: {:6.3} seconds'.format(f.__name__, t)
On my (iMac) machine:
f1: 2.95 seconds
f2: 1.45 seconds
f3: 1.97 seconds
Upvotes: 0
Reputation:
You can use the else
block of a for-loop:
>>> for i in [1, 2, 3, 4, 5]:
... print(i)
... else:
... print(i**2)
...
1
2
3
4
5
25
>>>
As you can see, an operation is performed on each element in the list but the last one undergoes an extra operation.
Note too that the else
block will only be run if the loop exits normally without encountering a break
statement. This behavior seems proper because, if a break
statement was encountered, then the loop was explicitly exited and we are done with the list.
Upvotes: 2
Reputation: 309929
for item in lst:
do_something_to(item)
else:
do_something_extra_special_to_last(item)
Here I just assume that you want to do something extra to the last item (the normal action will still be taken on it beforehand). I also assume you aren't hitting any break
statements (in that case else
won't execute). Of course, you don't really need else
:
for item in lst:
do_something_to(item)
do_something_extra_special_to_last(item)
should work too since the loop variable "leaks" into the enclosing scope and if there are breaks that you're worried about and you really are looping over a sequence, why not:
for item in lst:
do_something_to(item)
do_something_extra_special_to_last(lst[-1])
Upvotes: 11
Reputation: 122032
I would certainly prefer the second version of the two you present; index
could cause problems if there are duplicates in the list and is an O(n)
operation on every iteration, whereas len
is O(1)
.
Generally, though, as you want to do something additional (not different) to the last item, I would just make it a separate step after the for
loop:
for item in lst:
# do something to every element
# do something to lst[-1]
This will work even if there is a break
(unlike using else
) and affects the last item in the list not the last item iterated over - this may or may not be desired behaviour.
Upvotes: 1
Reputation: 28292
You're making up problems :) There really isn't any with your approach.
If you want to loop, you can find the length. And then access the last thing. Or just do the loop, then do something with a_list[-1]
. Fancy way, use for-else
- you can google it. But then again, really, there is nothing wrong with your code.
Upvotes: 4