Jaki jest pythoniczny sposób na wykrycie ostatniego elementu w pętli 'for'?

Chciałbym znać najlepszy sposób (bardziej zwarty i "pythoniczny") na specjalne traktowanie ostatniego elementu w pętli for. Istnieje fragment kodu, który powinien być wywołany tylko pomiędzy elementami, który jest tłumiony w ostatnim.

Oto Jak to obecnie robię:

for i, data in enumerate(data_list):
    code_that_is_done_for_every_element
    if i != len(data_list) - 1:
        code_that_is_done_between_elements
Jest jakiś lepszy sposób?

Uwaga: nie chcę robić tego za pomocą hacków, takich jak używanie reduce. ;)

Author: martineau, 2009-10-27

25 answers

W większości przypadków łatwiej (i taniej) zrobić pierwszą iterację specjalnym przypadkiem zamiast ostatnim:

first = True
for data in data_list:
    if first:
        first = False
    else:
        between_items()

    item()

To będzie działać dla każdego iterable, nawet dla tych, które nie mają len():

file = open('/path/to/file')
for line in file:
    process_line(line)

    # No way of telling if this is the last line!

Poza tym, nie sądzę, że istnieje ogólnie lepsze rozwiązanie, ponieważ zależy to od tego, co próbujesz zrobić. Na przykład, jeśli budujesz łańcuch znaków z listy, naturalnie lepiej jest użyć str.join() niż użyć pętli for ze specjalnymi case".


Używając tej samej zasady, ale bardziej zwartej:

for i, line in enumerate(data_list):
    if i > 0:
        between_items()
    item()
Wygląda znajomo, prawda? :)

Dla @ ofko i innych, którzy naprawdę muszą się dowiedzieć, czy aktualna wartość iterable without len() jest ostatnią, musisz spojrzeć w przyszłość:

def lookahead(iterable):
    """Pass through all values from the given iterable, augmented by the
    information if there are more values to come after the current one
    (True), or if it is the last value (False).
    """
    # Get an iterator and pull the first value.
    it = iter(iterable)
    last = next(it)
    # Run the iterator to exhaustion (starting from the second value).
    for val in it:
        # Report the *previous* value (more to come).
        yield last, True
        last = val
    # Report the last value.
    yield last, False

Wtedy możesz użyć go tak:

>>> for i, has_more in lookahead(range(3)):
...     print(i, has_more)
0 True
1 True
2 False
 165
Author: Ferdinand Beyer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-06 21:00:06

Chociaż to pytanie jest dość stare, trafiłem tutaj przez google i znalazłem dość prosty sposób: krojenie listy. Załóżmy, że chcesz umieścić " & " pomiędzy wszystkimi wpisami listy.

s = ""
l = [1, 2, 3]
for i in l[:-1]:
    s = s + str(i) + ' & '
s = s + str(l[-1])

Zwraca "1 & 2 & 3".

 25
Author: BeckmaR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-17 07:46:09

'code between' jest przykładem wzorca Head-Tail .

Masz element, po którym następuje Sekwencja par (pomiędzy, item). Możesz również wyświetlić to jako sekwencję par (item, between), a następnie element. Generalnie prostsze jest potraktowanie pierwszego elementu jako specjalnego, a wszystkich pozostałych jako" standardowego " przypadku.

Ponadto, aby uniknąć powtarzania kodu, musisz podać funkcję lub inny obiekt zawierający kod, którego nie chcesz powtarzać. Osadzenie If W pętli, która zawsze jest fałszywa, z wyjątkiem jednego razu jest trochę głupie.

def item_processing( item ):
    # *the common processing*

head_tail_iter = iter( someSequence )
head = next(head_tail_iter)
item_processing( head )
for item in head_tail_iter:
    # *the between processing*
    item_processing( item )

Jest to bardziej wiarygodne, ponieważ jest nieco łatwiejsze do udowodnienia, nie tworzy dodatkowej struktury danych (np. kopii listy) i nie wymaga dużo zmarnowanego wykonania warunku if, który jest zawsze fałszywy, z wyjątkiem jednego.

 19
Author: S.Lott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-15 15:56:59

Jeśli elementy są unikalne:

for x in list:
    #code
    if x == list[-1]:
        #code

Inne opcje:

pos = -1
for x in list:
    pos += 1
    #code
    if pos == len(list) - 1:
        #code


for x in list:
    #code
#code - e.g. print x


if len(list) > 0:
    for x in list[:-1]
        #code
    for x in list[-1]:
        #code
 18
Author: Palza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-07 00:16:02

Jeśli po prostu chcesz zmodyfikować ostatni element w data_list, możesz po prostu użyć notacji:

L[-1]
Jednak wygląda na to, że robisz coś więcej. Nie ma nic złego w Twoim sposobie. Rzuciłem nawet okiem na jakiś Kod Django dla ich znaczników szablonów i robią to, co robisz.
 17
Author: Bartek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-27 12:05:19

Jest to podobne do podejścia Ants Aasma, ale bez użycia modułu itertools. Jest to również opóźniony iterator, który patrzy przed siebie pojedynczy element w strumieniu iteratora:

def last_iter(it):
    # Ensure it's an iterator and get the first field
    it = iter(it)
    prev = next(it)
    for item in it:
        # Lag by one item so I know I'm not at the end
        yield 0, prev
        prev = item
    # Last item
    yield 1, prev

def test(data):
    result = list(last_iter(data))
    if not result:
        return
    if len(result) > 1:
        assert set(x[0] for x in result[:-1]) == set([0]), result
    assert result[-1][0] == 1

test([])
test([1])
test([1, 2])
test(range(5))
test(xrange(4))

for is_last, item in last_iter("Hi!"):
    print is_last, item
 12
Author: Andrew Dalke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-27 21:45:15

Możesz użyć przesuwanego okna nad danymi wejściowymi, aby zobaczyć następną wartość i użyć sentinel do wykrycia ostatniej wartości. Działa to na każdym iterable, więc nie musisz wcześniej znać długości. Implementacja parowa pochodzi z itertools recipes .

from itertools import tee, izip, chain

def pairwise(seq):
    a,b = tee(seq)
    next(b, None)
    return izip(a,b)

def annotated_last(seq):
    """Returns an iterable of pairs of input item and a boolean that show if
    the current item is the last item in the sequence."""
    MISSING = object()
    for current_item, next_item in pairwise(chain(seq, [MISSING])):
        yield current_item, next_item is MISSING:

for item, is_last_item in annotated_last(data_list):
    if is_last_item:
        # current item is the last item
 5
Author: Ants Aasma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-27 13:17:21

Czy nie ma możliwości iteracji wszystkich-ale ostatniego elementu i traktowania ostatniego poza pętlą? W końcu pętla jest tworzona, aby zrobić coś podobnego do wszystkich elementów, nad którymi zapętlasz; jeśli jeden element potrzebuje czegoś specjalnego, nie powinien być w pętli.

(Zobacz także to pytanie: czy-ostatni-element-w-pętli-zasługuje-na-oddzielne-traktowanie )

EDIT: ponieważ pytanie jest bardziej o "pomiędzy", albopierwszy element jest specjalny jednym z nich jest to, że nie ma poprzednika, lub Ostatni element jest wyjątkowy, ponieważ nie ma następcy.

 4
Author: xtofl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:31

Podoba mi się podejście @ethan-t, ale while True jest niebezpieczne z mojego punktu widzenia.

data_list = [1, 2, 3, 2, 1]  # sample data
L = list(data_list)  # destroy L instead of data_list
while L:
    e = L.pop(0)
    if L:
        print(f'process element {e}')
    else:
        print(f'process last element {e}')
del L

Tutaj data_list jest tak, że ostatni element jest równy wartości pierwszego z listy. L może być zamienione na data_list, ale w tym przypadku wynik jest pusty po pętli. while True jest również możliwe do użycia, jeśli sprawdzisz, że lista nie jest pusta przed przetwarzaniem lub sprawdzenie nie jest potrzebne(AU!).

data_list = [1, 2, 3, 2, 1]
if data_list:
    while True:
        e = data_list.pop(0)
        if data_list:
            print(f'process element {e}')
        else:
            print(f'process last element {e}')
            break
else:
    print('list is empty')

Dobre jest to, że jest szybki. Złe - jest destrukcyjne (data_list staje się pusta).

Najbardziej intuicyjne rozwiązanie:

data_list = [1, 2, 3, 2, 1]  # sample data
for i, e in enumerate(data_list):
    if i != len(data_list) - 1:
        print(f'process element {e}')
    else:
        print(f'process last element {e}')

O tak, już to zaproponowałeś!

 4
Author: Aliaksandr Klimovich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-04 21:54:04

Możesz określić ostatni element za pomocą tego kodu:

for i,element in enumerate(list):
    if (i==len(list)-1):
        print("last element is" + element)
 4
Author: hassanzadeh.sd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-18 07:21:14

Nie ma nic złego w Twoim sposobie, chyba że będziesz miał 100 000 pętli i chcesz zapisać 100 000 instrukcji "if". W takim razie możesz iść tą drogą:

iterable = [1,2,3] # Your date
iterator = iter(iterable) # get the data iterator

try :   # wrap all in a try / except
    while 1 : 
        item = iterator.next() 
        print item # put the "for loop" code here
except StopIteration, e : # make the process on the last element here
    print item

Wyjścia:

1
2
3
3
Ale naprawdę, w Twoim przypadku czuję, że to przesada.

W każdym razie, prawdopodobnie będziesz miał więcej szczęścia z krojeniem:

for item in iterable[:-1] :
    print item
print "last :", iterable[-1]

#outputs
1
2
last : 3

Lub po prostu:

for item in iterable :
    print item
print iterable[-1]

#outputs
1
2
3
last : 3

W końcu, sposób na pocałunek, który działa z każdą iteracją, w tym z tymi bez __len__ :

item = ''
for item in iterable :
    print item
print item

Ouputs:

1
2
3
3
Jeśli czuję, że zrobię to w ten sposób, wydaje mi się to proste.
 3
Author: e-satis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-28 11:56:24

Użyj krojenia i is, aby sprawdzić ostatni element:

for data in data_list:
    <code_that_is_done_for_every_element>
    if not data is data_list[-1]:
        <code_that_is_done_between_elements>

Caveat emptor : to działa tylko wtedy, gdy wszystkie elementy na liście są rzeczywiście różne (mają różne lokalizacje w pamięci). Pod maską Python może wykrywać równe elementy i ponownie używać dla nich tych samych obiektów. Na przykład dla ciągów o tej samej wartości i wspólnych liczb całkowitych.

 3
Author: Roger Dahl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-28 17:41:17

Google doprowadziło mnie do tego starego pytania i myślę, że mogę dodać inne podejście do tego problemu.

Większość odpowiedzi tutaj zajmowałaby się prawidłowym traktowaniem kontroli pętli for, jak to zostało zadane, ale jeśli data_list jest destruktywna, sugerowałbym, abyś wyskakiwał elementy z listy, aż skończysz z pustą listą:

while True:
    element = element_list.pop(0)
    do_this_for_all_elements()
    if not element:
        do_this_only_for_last_element()
        break
    do_this_for_all_elements_but_last()

Możesz nawet użyć while len (element_list) jeśli nie musisz nic robić z ostatnim elementem. Znajduję takie rozwiązanie bardziej elegancki niż do czynienia z next().

 3
Author: Anderson Santos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-14 14:09:10

Lepiej późno niż wcale. Twój oryginalny kod użył enumerate(), ale użyłeś tylko indeksu i, aby sprawdzić, czy jest to ostatnia Pozycja na liście. Oto prostsza alternatywa (jeśli nie potrzebujesz enumerate()) przy użyciu indeksowania ujemnego:

for data in data_list:
    code_that_is_done_for_every_element
    if data != data_list[-1]:
        code_that_is_done_between_elements

if data != data_list[-1] sprawdza, czy bieżąca pozycja w iteracji nie jest ostatnią pozycją na liście.

Mam nadzieję, że to pomoże, nawet prawie 11 lat później.
 3
Author: howdoicode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-03 19:26:03

Jeśli przeglądasz listę, dla mnie to też zadziałało:

for j in range(0, len(Array)):
    if len(Array) - j > 1:
        notLast()
 2
Author: tsf144,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-08 21:51:37

Dla mnie najprostszym i najprostszym sposobem obsługi specjalnego przypadku na końcu listy jest:

for data in data_list[:-1]:
    handle_element(data)
handle_special_element(data_list[-1])

Oczywiście może to być również wykorzystane do potraktowania pierwszego elementu w szczególny sposób .

 2
Author: Chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-19 11:55:48

Zamiast liczyć w górę, Możesz również odliczać w dół:

  nrToProcess = len(list)
  for s in list:
    s.doStuff()
    nrToProcess -= 1
    if nrToProcess==0:  # this is the last one
      s.doSpecialStuff()
 2
Author: jeroent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-19 17:37:29

Opóźnij specjalną obsługę ostatniego elementu aż do zakończenia pętli.

>>> for i in (1, 2, 3):
...     pass
...
>>> i
3
 1
Author: user240515,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-01 22:54:36

Może być wiele sposobów. krojenie będzie najszybsze. Dodanie jeszcze jednego, który używa .metoda index ():

>>> l1 = [1,5,2,3,5,1,7,43]                                                 
>>> [i for i in l1 if l1.index(i)+1==len(l1)]                               
[43]
 1
Author: amolpachpute,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-01 06:46:32

Jedno proste rozwiązanie, które przychodzi na myśl, to:

for i in MyList:
    # Check if 'i' is the last element in the list
    if i == MyList[-1]:
        # Do something different for the last
    else:
        # Do something for all other elements

Drugie równie proste rozwiązanie można uzyskać za pomocą licznika:

# Count the no. of elements in the list
ListLength = len(MyList)
# Initialize a counter
count = 0

for i in MyList:
    # increment counter
    count += 1
    # Check if 'i' is the last element in the list
    # by using the counter
    if count == ListLength:
        # Do something different for the last
    else:
        # Do something for all other elements
 1
Author: AnythingIsFine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-16 08:56:21

Po prostu sprawdź, czy dane nie są takie same jak ostatnie dane w data_list (data_list[-1]).

for data in data_list:
    code_that_is_done_for_every_element
    if data != data_list[- 1]:
        code_that_is_done_between_elements
 1
Author: Risyad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-24 02:47:28

Zakładając wejście jako iterator, oto sposób na użycie tee i izip z itertools:

from itertools import tee, izip
items, between = tee(input_iterator, 2)  # Input must be an iterator.
first = items.next()
do_to_every_item(first)  # All "do to every" operations done to first item go here.
for i, b in izip(items, between):
    do_between_items(b)  # All "between" operations go here.
    do_to_every_item(i)  # All "do to every" operations go here.

Demo:

>>> def do_every(x): print "E", x
...
>>> def do_between(x): print "B", x
...
>>> test_input = iter(range(5))
>>>
>>> from itertools import tee, izip
>>>
>>> items, between = tee(test_input, 2)
>>> first = items.next()
>>> do_every(first)
E 0
>>> for i,b in izip(items, between):
...     do_between(b)
...     do_every(i)
...
B 0
E 1
B 1
E 2
B 2
E 3
B 3
E 4
>>>
 0
Author: Anon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-27 13:39:40

Najprostsze rozwiązanie przychodzi mi do głowy to:

for item in data_list:
    try:
        print(new)
    except NameError: pass
    new = item
print('The last item: ' + str(new))

Więc zawsze patrzymy do przodu jeden przedmiot, opóźniając przetwarzanie jednej iteracji. Aby pominąć robienie czegoś podczas pierwszej iteracji, po prostu wyłapuję błąd.

Oczywiście musisz trochę pomyśleć, aby NameError zostały podniesione, kiedy chcesz.

Również zachować ' counsruct

try:
    new
except NameError: pass
else:
    # continue here if no error was raised

Polega to na tym, że nazwa new nie była wcześniej zdefiniowana. Jeśli masz paranoję, możesz upewnić się, że new nie exist using:

try:
    del new
except NameError:
    pass

Alternatywnie można oczywiście również użyć instrukcji if (if notfirst: print(new) else: notfirst = True). Ale z tego co wiem, koszty są większe.


Using `timeit` yields:

    ...: try: new = 'test' 
    ...: except NameError: pass
    ...: 
100000000 loops, best of 3: 16.2 ns per loop
Więc spodziewam się, że napowietrzność będzie Nieelektryczna.
 0
Author: DerWeh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-03 13:45:23

Policz przedmioty raz i nadążaj za liczbą pozostałych przedmiotów:

remaining = len(data_list)
for data in data_list:
    code_that_is_done_for_every_element

    remaining -= 1
    if remaining:
        code_that_is_done_between_elements

W ten sposób oceniasz długość listy tylko raz. Wiele rozwiązań na tej stronie wydaje się zakładać, że długość jest niedostępna z góry, ale nie jest to częścią twojego pytania. Jeśli masz długość, użyj go.

 0
Author: Ethan T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 20:04:20

Więc, to na pewno nie jest" krótsza "wersja - i można dygresję, jeśli "Najkrótsza" i "Pythonic" są rzeczywiście zgodne.

Ale jeśli ktoś potrzebuje tego wzoru często, wystarczy umieścić logikę w Generator 10-liniowy-i uzyskać wszelkie metadane związane z elementem pozycję bezpośrednio na wywołaniu for. Kolejną zaletą jest to, że będzie praca z dowolnymi iterowalnymi, nie tylko sekwencjami.

_sentinel = object()

def iter_check_last(iterable):
    iterable = iter(iterable)
    current_element = next(iterable, _sentinel)
    while current_element is not _sentinel:
        next_element = next(iterable, _sentinel)
        yield (next_element is _sentinel, current_element)
        current_element = next_element
In [107]: for is_last, el in iter_check_last(range(3)):
     ...:     print(is_last, el)
     ...: 
     ...: 
False 0
False 1
True 2
 0
Author: jsbueno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-15 15:53:52