195 votes

Itération d'une liste par paire (current, next) en Python

J'ai parfois besoin d'itérer une liste en Python en regardant l'élément "courant" et l'élément "suivant". Jusqu'à présent, je l'ai fait avec du code comme :

for current, next in zip(the_list, the_list[1:]):
    # Do something

Cela fonctionne et fait ce que j'attends, mais y a-t-il une façon plus idiomatique ou efficace de faire la même chose ?

0 votes

Consultez la réponse de MizardX pour cette question . Mais je ne pense pas que cette solution soit plus idiomatique que la vôtre.

2 votes

Jetez un coup d'œil à Construire un itérateur Python de base .

48 votes

Puisque personne d'autre ne l'a mentionné, je vais être ce type de personne et signaler que l'utilisation de la next cette façon de masquer un intégré.

178voto

Rafał Dowgird Points 16600

Voici un exemple pertinent tiré du itertools docs du module :

import itertools
def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = itertools.tee(iterable)
    next(b, None)
    return zip(a, b)   

Pour Python 2, vous avez besoin de itertools.izip au lieu de zip :

import itertools
def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = itertools.tee(iterable)
    next(b, None)
    return itertools.izip(a, b)

Comment cela fonctionne :

Tout d'abord, deux itérateurs parallèles, a y b sont créés (le tee() ), tous deux pointant vers le premier élément de l'itérable original. Le deuxième itérateur, b est déplacé d'un pas en avant (le next(b, None) ) appel). À ce stade a pointe vers s0 et b pointe vers s1. Les deux sites a y b peut parcourir l'itérateur original indépendamment - la fonction izip prend les deux itérateurs et crée des paires d'éléments retournés, faisant avancer les deux itérateurs au même rythme.

Une mise en garde : le tee() produit deux itérateurs qui peuvent avancer indépendamment l'un de l'autre, mais cela a un coût. Si l'un des itérateurs avance plus loin que l'autre, alors tee() doit conserver les éléments consommés en mémoire jusqu'à ce que le second itérateur les consomme également (il ne peut pas "rembobiner" l'itérateur original). Ici, cela n'a pas d'importance car un itérateur n'a qu'un pas d'avance sur l'autre, mais en général, il est facile d'utiliser beaucoup de mémoire de cette façon.

Et comme tee() peut prendre un n ce paramètre peut également être utilisé pour plus de deux itérateurs parallèles :

def threes(iterator):
    "s -> (s0,s1,s2), (s1,s2,s3), (s2, s3,4), ..."
    a, b, c = itertools.tee(iterator, 3)
    next(b, None)
    next(c, None)
    next(c, None)
    return zip(a, b, c)

13 votes

zip(, [1:]) est beaucoup plus court et pythonique

12 votes

@noz : Non, cela ne fonctionne pas sur chaque itérable et fait une copie inutile lorsqu'il est utilisé sur des listes. Utiliser les fonctions est pythonique.

0 votes

Cette fonction est mise en œuvre dans funcy module : funcy.pairwise : funcy.readthedocs.io/fr/stable/seqs.html#pairwise

57voto

minitech Points 87225

Roulez le vôtre !

def pairwise(iterable):
    it = iter(iterable)
    a = next(it, None)

    for b in it:
        yield (a, b)
        a = b

2 votes

Juste ce dont j'avais besoin ! Est-ce que cela a été immortalisé comme une méthode python, ou devons-nous continuer à rouler ?

2 votes

@uhoh : Pas encore, pour autant que je sache !

0 votes

Je suis surpris que ce ne soit pas la réponse acceptée. Pas d'importations et la logique derrière est très facile à comprendre. +1 définitivement.

31voto

thecoder16 Points 144

Je mets juste ça en avant, Je suis très surpris que personne n'ait pensé à enumerate().

for (index, thing) in enumerate(the_list):
    if index < len(the_list):
        current, next_ = thing, the_list[index + 1]
        #do something

18 votes

En fait, le if peut également être enlevé si vous utilisez le tranchage : for (index, thing) in enumerate(the_list[:-1]): current, next_ = thing, the_list[index + 1]

4 votes

Cela devrait vraiment être la solution, elle ne repose sur aucune importation supplémentaire et fonctionne très bien.

6 votes

Cependant, cela ne fonctionne pas pour les itérables non-indexables, ce n'est donc pas une solution générique.

24voto

Sven Marnach Points 133943

Depuis the_list[1:] crée en fait une copie de la liste entière (à l'exception de son premier élément), et zip() crée une liste de tuples immédiatement lorsqu'il est appelé, au total trois copies de votre liste sont créées. Si votre liste est très grande, vous pouvez préférer

from itertools import izip, islice
for current_item, next_item in izip(the_list, islice(the_list, 1, None)):
    print(current_item, next_item)

qui ne copie pas du tout la liste.

4 votes

Notez que dans python 3.x izip est supprimé de itertools et que vous devez utiliser le zip intégré.

2 votes

En fait, n'est-ce pas the_list[1:] crée simplement un objet tranche plutôt qu'une copie de la quasi-totalité de la liste - donc la technique de l'OP n'est pas aussi inutile que vous le dites.

3 votes

Je pense [1:] crée l'objet tranche (ou éventuellement " 1: "), qui est transmis à __slice__ sur la liste, qui renvoie alors une copie contenant uniquement les éléments sélectionnés. Une façon idiomatique de copier une liste est la suivante l_copy = l[:] (que je trouve laid et illisible -- je préfère l_copy = list(l) )

17voto

Rumple Stiltskin Points 2309

L'itération par index peut faire la même chose :

#!/usr/bin/python
the_list = [1, 2, 3, 4]
for i in xrange(len(the_list) - 1):
    current_item, next_item = the_list[i], the_list[i + 1]
    print(current_item, next_item)

Sortie :

(1, 2)
(2, 3)
(3, 4)

0 votes

Votre réponse était plus précédent y actuel au lieu de actuel y suivant comme dans la question. J'ai fait une modification pour améliorer la sémantique de sorte que i est toujours l'index de l'élément courant.

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