Выполните итерацию по списку Python и сделайте что-нибудь с последним элементом

#python

#python

Вопрос:

Я ищу питонический способ перебора списка и выполнения чего-либо с последним (и только последним) элементом. Я вижу два способа сделать это, из которых, я бы предположил, что второй лучше:

 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
 

и

 for n, item in enumerate(a_list):
   #do something to every element
   if n == len(a_list) - 1 :
       # do something to the last one
 

Однако мне интересно, есть ли способ сделать это без вызова len() списка, который я уже перебираю. Кстати, я очень рад, что мне сказали, что это не то, о чем я должен беспокоиться.

Комментарии:

1. Можете ли вы объяснить разницу между «выполнить итерацию по списку и сделать что-то с последним (и только последним) элементом» и просто сделать что-то с a_list[-1] ?

2. Просто казалось стыдным просматривать список, а затем возвращаться и делать что-то с последним элементом, вместо того, чтобы выполнять их оба на одной итерации. Как и выше, это может быть основано на эмоциях; но полезно узнать, когда это правда.

3. Вы также можете сделать enumerate(a_list, 1) , и тогда вам не нужно выполнять вычитание при проверке каждого шага n == len(a_list) .

4. @mgilson — полезно, я об этом не знал.

Ответ №1:

 for item in lst:
   do_something_to(item)
else:
   do_something_extra_special_to_last(item)
 

Здесь я просто предполагаю, что вы хотите сделать что-то дополнительное с последним элементом (обычное действие все равно будет выполнено для него заранее). Я также предполагаю, что вы не выполняете никаких break инструкций (в этом случае else они не будут выполняться). Конечно, вам действительно не нужно else :

 for item in lst:
    do_something_to(item)
do_something_extra_special_to_last(item)
 

тоже должно сработать, поскольку переменная цикла «просачивается» во охватывающую область, и если есть перерывы, о которых вы беспокоитесь, и вы действительно перебираете последовательность, почему бы и нет:

 for item in lst:
   do_something_to(item)
do_something_extra_special_to_last(lst[-1])
 

Комментарии:

1. else Хотя использование блока может быть лучшим вариантом. Если мы явно нарушили цикл break , последний элемент не должен обрабатываться IMO. Цикл завершен, и мы закончили lst .

2. @iCodez — Возможно. Это действительно зависит от проблемы, которую пытается решить OP (которая не была определена очень хорошо), поэтому я попытался предложить решение для ряда различных вариантов проблемы. 🙂

Ответ №2:

Вы создаете проблемы 🙂 В вашем подходе действительно нет ничего подобного.

Если вы хотите выполнить цикл, вы можете найти длину. А затем получите доступ к последнему элементу. Или просто выполните цикл, а затем сделайте что-нибудь с a_list[-1] . Необычный способ, используйте for-else — вы можете его погуглить. Но опять же, на самом деле, в вашем коде нет ничего плохого.

Ответ №3:

Вы можете использовать else блок for-цикла:

 >>> for i in [1, 2, 3, 4, 5]:
...     print(i)
... else:
...     print(i**2)
...
1
2
3
4
5
25
>>>
 

Как вы можете видеть, операция выполняется над каждым элементом в списке, но последний подвергается дополнительной операции.

Также обратите внимание, что else блок будет запущен только в том случае, если цикл завершается нормально, не встречая break оператора. Такое поведение кажется правильным, потому что, если break был обнаружен оператор, цикл был явно завершен, и мы закончили со списком.

Ответ №4:

Вы можете использовать это:

 a_list[-1]
 

для доступа к последнему элементу

Комментарии:

1. a_list[:-1] даст мне список до последнего элемента. Я думаю, вы думаете о a_list[-1]

2. @FarmerGedden, ты только что ответил на свой вопрос?

3. Нет. Я знаю, как получить последний элемент списка. Я бы хотел, чтобы при итерации по списку был простой способ сделать что-то только с последним элементом.

4. @FarmerGedden вероятно, проще всего просто включить это после цикла, а не играть с индексами.

Ответ №5:

Я бы, конечно, предпочел вторую версию из двух, которые вы представляете; index может вызвать проблемы, если в списке есть дубликаты, и это O(n) операция на каждой итерации, тогда len как is O(1) .

Как правило, однако, поскольку вы хотите сделать что-то дополнительное (не отличающееся) от последнего элемента, я бы просто сделал это отдельным шагом после for цикла:

 for item in lst:
    # do something to every element
# do something to lst[-1]
 

Это будет работать, даже если есть break (в отличие от использования else ) и влияет на последний элемент в списке, а не на последний элемент, который повторяется — это может быть или не быть желательным поведением.

Ответ №6:

Рассмотрим:

 li[:]=[do_somthing(item) for item in li]  # something to every item in place
li[-1]=something_extra(li[-1])            # additional to last item
 

против

 for i, item in enumerate(li):
    li[i]=do_somthing(item)
    if i==len(li)-1:
        li[i]=something_extra(item)
 

Если вы рассчитаете это время, вы увидите, что это самый быстрый способ:

 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)
 

На моем компьютере (iMac):

 f1:   2.95 seconds
f2:   1.45 seconds
f3:   1.97 seconds