274 votes

Quel est le moyen pythonique de détecter le dernier élément d'une boucle 'pour' en python?

J'aimerais connaître le meilleur moyen (plus compact et "pythonique") de faire un traitement spécial pour le dernier élément d'une boucle for. Il y a un morceau de code qui devrait être appelé uniquement entre les éléments, étant supprimé dans le dernier.

Voici comment je le fais actuellement:

 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
 

Y a-t-il une meilleure façon?

Note: Je ne veux pas le faire avec des hacks comme utiliser reduce ;)

196voto

Ferdinand Beyer Points 27723

La plupart du temps, il est plus facile (et moins cher) de faire la première itération, le cas spécial au lieu de la dernière:

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

    item()

Cela fonctionne pour n'importe quel itérable, même pour ceux qui n'ont pas d' len():

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

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

En dehors de cela, je ne pense pas, il est généralement supérieur à la solution, car il dépend de ce que vous essayez de faire. Par exemple, si vous êtes à la construction d'une chaîne à partir d'une liste, il est naturellement préférable d'utiliser str.join() que l'utilisation d'un for boucle "avec cas spécial".


En utilisant le même principe, mais plus compact:

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

Semble familier, n'est-ce pas? :)


Pour @ofko, et d'autres qui en ont vraiment besoin pour savoir si la valeur actuelle d'un objet iterable sans len() est le dernier, vous aurez besoin de regarder à l'avance:

def lookahead(iterable):
    it = iter(iterable)
    last = it.next() # next(it) in Python 3
    for val in it:
        yield last, False
        last = val
    yield last, True

Ensuite, vous pouvez l'utiliser comme ceci:

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

21voto

S.Lott Points 207588

Le code entre " est un exemple de la Tête de Queue de modèle.

Vous avez un élément, qui est suivie par une séquence de ( entre, élément ) paires. Vous pouvez aussi voir cela comme une séquence de (point, entre les deux) paires suivi par un point. Il est généralement plus simple de prendre le premier élément spécial et tous les autres comme la "norme".

De plus, pour éviter la répétition de code, vous devez fournir une fonction ou d'un autre objet pour contenir le code que vous ne voulez pas de répéter. L'intégration d'un si déclaration en boucle, ce qui est toujours faux, sauf un, le temps est un peu idiot.

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 )

Ce qui est plus fiable parce que c'est un peu plus facile à prouver, Il n'est pas de créer une structure de données supplémentaires (c'est à dire, une copie d'une liste) et ne nécessite pas beaucoup de gaspillage de l'exécution d'un si condition est toujours fausse, sauf une fois.

21voto

Bartek Points 7491

Si vous souhaitez simplement modifier le dernier élément de data_list vous pouvez simplement utiliser la notation:

 L[-1]
 

Cependant, il semble que vous fassiez plus que cela. Il n'y a rien de mal à votre façon. J'ai même jeté un coup d'œil à du code Django pour leurs balises de modèle et ils font essentiellement ce que vous faites.

12voto

Andrew Dalke Points 7607

Ceci est similaire à l'approche de Ants Aasma mais sans utiliser le module itertools. C'est aussi un itérateur en retard qui regarde en avant un seul élément dans le flux d'itérateurs:

 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
 

4voto

xtofl Points 22333

Il n'est pas possible d'itérer sur tous, mais le dernier élément, et de traiter le dernier à l'extérieur de la boucle? Après tout, une boucle est créée pour faire la même chose pour tous les éléments d'une boucle; si un élément a besoin de quelque chose de spécial, il ne devrait pas être dans la boucle.

(voir aussi cette question: n'a-la-dernière-élément-dans-une-boucle-méritent-un-séparée-traitement)

EDIT: étant donné que la question est plus sur l ' "entre-deux", soit le premier élément est celui qui est spécial en ce qu'il n'a pas de prédécesseur, ou le dernier élément est particulier en ce qu'il n'a pas de successeur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X