Was ist die pythonic Art und Weise das letzte Element in einem Python ‚für‘ Schleife zu erkennen?

StackOverflow https://stackoverflow.com/questions/1630320

Frage

Ich mag die beste Art und Weise wissen (kompakte und „pythonic“ Art und Weise), die eine spezielle Behandlung für das letzte Element in einem for-Schleife zu tun. Es ist ein Stück Code, der nur aufgerufen werden, sollte zwischen -Elemente, in der letzten unterdrückt wird.

Hier ist, wie ich es zur Zeit:

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

Gibt es einen besseren Weg?

Hinweis: Ich will nicht, es mit Hacks machen wie mit reduce;)

War es hilfreich?

Lösung

Die meisten der Zeit ist es einfacher (und billiger) die zuerst Iteration den Sonderfall zu machen, statt der zuletzt:

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

    item()

Dies ist für jede iterable arbeiten, auch für diejenigen, die keine len() haben:

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

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

Abgesehen davon, dass ich glaube nicht, dass es eine allgemein bessere Lösung ist, wie es hängt davon ab, was Sie zu tun versuchen. Zum Beispiel, wenn Sie eine Zeichenfolge aus einer Liste erstellen, ist es natürlich besser str.join() zu verwenden, als eine for Schleife „mit Sonderfall“ verwendet wird.


Nach dem gleichen Prinzip, aber kompakter:

for i, line in enumerate(data_list):
    if i > 0:
        between_items()
    item()

kommt mir bekannt vor, nicht wahr? :)


Für @ofko, und andere, die wirklich herausfinden müssen, wenn der aktuelle Wert eines iterable ohne len() die letzte ist, müssen Sie nach vorne schauen:

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

Dann können Sie es wie folgt verwendet werden:

>>> for i, has_more in lookahead(range(3)):
...     print(i, has_more)
0 True
1 True
2 False

Andere Tipps

Der 'Code zwischen' ist ein Beispiel für den Kopf-Schwanz Muster.

Sie haben ein Element, das durch eine Folge von (zwischen Punkt) Paaren folgt. Sie können dies auch als eine Folge von sehen (Punkt, zwischen) von einem Punkt gefolgt Paaren. Es ist in der Regel einfacher, das erste Element als Sondermüll zu nehmen und alle andere als der „Standard“ Fall.

Ferner, zu wiederholen Code zu vermeiden, müssen Sie eine Funktion oder ein anderes Objekt schaffen, um den Code zu enthalten, die Sie nicht wollen, zu wiederholen. Einbetten eine , wenn Anweisung in einer Schleife, die immer falsch, außer einer Zeit ist ein bisschen albern.

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

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

Dies ist zuverlässiger, da es zu beweisen, etwas einfacher ist, es keine zusätzliche Datenstruktur erstellen (dh eine Kopie einer Liste) und nicht eine Menge vergeudeter Ausführung eines verlangen, wenn Bedingung, die außer einmal immer falsch ist.

Wenn Sie einfach nun das letzte Element in data_list zu ändern, dann können Sie einfach die Notation:

L[-1]

Allerdings sieht es aus wie Sie mehr als das zu tun. Es gibt nichts wirklich falsch mit Ihrem Weg. Ich habe sogar einen kurzen Blick auf einige Django Code ihre Template-Tags und sie tun im Grunde, was Sie tun.

Auch wenn diese Frage ziemlich alt ist, kam ich hier über google und ich fand eine ganz einfache Art und Weise: Liste schneiden. Angenommen, Sie haben ein ‚&‘ zwischen allen Listeneinträge setzen wollen.

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

Das gibt '1 & 2 & 3'.

Dies ist ähnlich wie Ameisen Aasma Ansatz, aber ohne das itertools Modul. Es ist auch eine nacheilende Iterator, die ein einzelnes Element im Iterator-Stream-ahead aussieht:

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

, wenn die Einzelteile sind einzigartig:

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

weitere Optionen:

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

Sie können ein Schiebefenster über die Eingabedaten verwenden, um einen Blick auf dem nächsten Wert zu erhalten und einen Sentinel verwenden, um den letzten Wert zu ermitteln. Dies funktioniert auf jedem iterable, so dass Sie nicht die Länge vorher wissen müssen. Die paarweise Implementierung von itertools Rezepte .

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

Gibt es keine Möglichkeit, all-aber iterieren das letzte Element, und behandeln die letzten außerhalb der Schleife? Schließlich wird eine Schleife erstellt über etwas ähnliches für alle Elemente, die Sie Schleife zu tun; wenn ein Element etwas Besonderes braucht, sollte es nicht in der Schleife sein.

(siehe auch diese Frage: tut -Der-last-Element-in-a-loop-verdienen-a-getrennte Behandlungs )

EDIT: da die Frage ist, mehr über die „zwischendurch“, entweder die zuerst Element ist die besondere dadurch, dass es keinen Vorgänger hat, oder das letzte -Element in ist etwas besonderes, dass es keinen Nachfolger hat.

Es ist nichts falsch mit dem Weg, wenn Sie 100 000 Schleifen haben und wollen 100 000 „wenn“ Aussagen speichern. In diesem Fall können Sie diesen Weg gehen:

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

Ausgänge:

1
2
3
3

Aber wirklich, in Ihrem Fall fühle ich mich wie es ist viel des Guten.

In jedem Fall werden Sie wahrscheinlich mit Slicing mehr Glück:

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

#outputs
1
2
last : 3

oder einfach nur:

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

#outputs
1
2
3
last : 3

Schließlich ein KISS Art und Weise zu tun, stopfen Sie, und das mit jeder iterable funktionieren würde, auch diejenigen ohne __len__:

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

Ausgänge:

1
2
3
3

Wenn das Gefühl, ich würde es auf diese Weise zu tun, mir scheint einfach.

Mit Aufschneiden und is für das letzte Element zu überprüfen:

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 : Dies funktioniert nur, wenn alle Elemente in der Liste tatsächlich unterschiedlich sind (unterschiedliche Stellen im Speicher). Unter der Haube kann Python gleiche Elemente erkennen und die gleichen Objekte für sie wiederverwenden. Zum Beispiel für Strings mit dem gleichen Wert und gemeinsamen ganzen Zahlen sind.

Google brachte mich auf diese alte Frage, und ich glaube, ich könnte einen anderen Ansatz für dieses Problem hinzuzufügen.

hier Die meisten Antworten mit einer richtigen Behandlung eines für die Regelung befassen würde, wie es gefragt wurde, aber wenn die daten_liste zerstörbar ist, würde ich vorschlagen, dass Sie die Elemente aus der Liste Pop, bis Sie mit einer leeren Liste am Ende :

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()

Sie können auch verwenden während len (element_list) , wenn Sie brauchen nichts mit dem letzten Element zu tun. Ich finde diese Lösung elegante dann mit dem nächsten zu tun ().

Ich mag den Ansatz von @ ethan-t, aber while True ist gefährlich, aus meiner Sicht.

while L:
    e = L.pop(0)
    # process element
    if not L:
        print('Last element has been detected.')

verzögern die besondere Behandlung des letzten Elements, bis nach der Schleife.

>>> for i in (1, 2, 3):
...     pass
...
>>> i
3

als Iterator Unter der Annahme, Eingang, hier ist eine Möglichkeit, mit Tee und izip von 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
>>>

Wenn Sie durch die Liste gehen, für mich gearbeitet zu:

for j in range(0, len(Array)):
    if len(Array) - j > 1:
        notLast()

Die einfachste Lösung meiner Meinung nach kommt, ist:

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

So vorausschauen ein Element wir immer durch die die Verarbeitung einer Iteration zu verzögern. Zum Überspringen etwas während der ersten Iteration tut ich einfach den Fehler zu fangen.

Natürlich müssen Sie ein wenig denken, um für die NameError angehoben werden, wenn Sie es wollen.

Auch halten die `counstruct

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

Dies beruht, dass der Name neu war zuvor nicht definiert. Wenn Sie paranoid sind, können Sie diese new gewährleisten nicht existiert mit:

try:
    del new
except NameError:
    pass

Alternativ können Sie natürlich auch eine if-Anweisung (if notfirst: print(new) else: notfirst = True) verwenden. Aber soweit ich weiß, der Aufwand ist größer.


Using `timeit` yields:

    ...: try: new = 'test' 
    ...: except NameError: pass
    ...: 
100000000 loops, best of 3: 16.2 ns per loop

so erwarte ich, dass der Overhead unwählbar sein.

die Elemente einmal Graf und mit der Anzahl der Elemente halten verbleibenden:

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

Auf diese Weise kann nur die Länge der Liste einmal bewerten. Viele der Lösungen, die auf dieser Seite erscheinen die Länge ist im Voraus nicht verfügbar zu übernehmen, aber das ist nicht Teil Ihrer Frage. Wenn Sie die Länge haben, verwenden Sie es.

Für mich ist die einfachste und pythonic Weg, um einen Sonderfall am Ende einer Liste zu handhaben ist:

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

Dies ist natürlich auch verwendet werden kann, um das erste Element in besonderer Weise zu behandeln.

Es kann mehrere Wege geben. Slicing wird am schnellsten. Hinzufügen eines weiteren, die verwendet .index () Methode:

>>> l1 = [1,5,2,3,5,1,7,43]                                                 
>>> [i for i in l1 if l1.index(i)+1==len(l1)]                               
[43]
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top