#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