web-dev-qa-db-fra.com

Python - Valeurs précédentes et suivantes dans une boucle

Comment puis-je faire comme ça en python?

foo = somevalue
previous = next = 0

for (i=1; i<objects.length(); i++) {
    if (objects[i]==foo){
        previous = objects[i-1]
        next = objects[i+1]
    }
}
62
dir01

Cela devrait faire l'affaire.

foo = somevalue
previous = next_ = None
l = len(objects)
for index, obj in enumerate(objects):
    if obj == foo:
        if index > 0:
            previous = objects[index - 1]
        if index < (l - 1):
            next_ = objects[index + 1]

Voici la documentation sur la fonction enumerate .

70
Hank Gay

Jusqu'à présent, les solutions ne traitent que des listes, et la plupart les copient. D'après mon expérience, cela n'est souvent pas possible.

En outre, ils ne traitent pas du fait que vous pouvez avoir des éléments répétés dans la liste.

Le titre de votre question indique "Valeurs précédentes et suivantes dans une boucle", mais si vous exécutez la plupart des réponses ici dans une boucle, vous finirez par parcourir à nouveau la liste complète sur chaque élément pour la trouver.

Donc, je viens de créer une fonction qui. en utilisant le module itertools , divise et divise l’itéré en tranches et génère des n-uplets avec les éléments précédent et suivant ensemble. Ce n'est pas exactement ce que fait votre code, mais cela vaut la peine de jeter un coup d'œil, car cela peut probablement résoudre votre problème.

from itertools import tee, islice, chain, izip

def previous_and_next(some_iterable):
    prevs, items, nexts = tee(some_iterable, 3)
    prevs = chain([None], prevs)
    nexts = chain(islice(nexts, 1, None), [None])
    return izip(prevs, items, nexts)

Puis utilisez-le en boucle et vous y trouverez les éléments précédents et suivants:

mylist = ['banana', 'orange', 'Apple', 'kiwi', 'tomato']

for previous, item, nxt in previous_and_next(mylist):
    print "Item is now", item, "next is", nxt, "previous is", previous

Les resultats:

Item is now banana next is orange previous is None
Item is now orange next is Apple previous is banana
Item is now Apple next is kiwi previous is orange
Item is now kiwi next is tomato previous is Apple
Item is now tomato next is None previous is kiwi

Cela fonctionnera avec n'importe quelle liste de taille (car il ne copie pas la liste), et avec n'importe quelle liste (fichiers, ensembles, etc.). De cette façon, vous pouvez simplement parcourir la séquence et avoir les éléments précédents et suivants disponibles dans la boucle. Pas besoin de chercher à nouveau l'élément dans la séquence.

Une brève explication du code:

  • tee est utilisé pour créer efficacement 3 itérateurs indépendants sur la séquence d'entrée
  • chain relie deux séquences en une seule; il est utilisé ici pour ajouter une séquence mono-élément [None] à prevs
  • islice est utilisé pour créer une séquence de tous les éléments sauf le premier, puis chain est utilisé pour ajouter une None à sa fin.
  • Il existe maintenant 3 séquences indépendantes basées sur some_iterable qui ressemblent à:
    • prevs: None, A, B, C, D, E
    • items: A, B, C, D, E
    • nexts: B, C, D, E, None
  • enfin, izip est utilisé pour changer 3 séquences en une séquence de triplets.

Notez que izip s’arrête lorsque toute séquence d’entrée est épuisée. Le dernier élément de prevs sera donc ignoré, ce qui est correct. Il n’existe pas d’élément tel que le dernier élément serait sa prev. Nous pourrions essayer de retirer les derniers éléments de prevs mais le comportement de izip rend cela redondant 

Notez également que tee, izip, islice et chain proviennent du module itertools; ils agissent sur leurs séquences d’entrée à la volée (paresseusement), ce qui les rend efficaces et n’introduit pas la nécessité d’avoir la séquence complète en mémoire en même temps, à tout moment.

Dans python 3, une erreur s'affichera lors de l'importation de izip, vous pouvez utiliser Zip au lieu de izip. Pas besoin d'importer Zip, il est prédéfini dans python 3 - source

119
nosklo

En utilisant une liste de compréhension, retourne un 3-Tuple avec les éléments actuels, précédents et suivants:

three_Tuple = [(current, 
                my_list[idx - 1] if idx >= 1 else None, 
                my_list[idx + 1] if idx < len(my_list) - 1 else None) for idx, current in enumerate(my_list)]
4
RYS

Voici une version utilisant des générateurs sans erreur de limite:

def trios(input):
    input = iter(input) # make sure input is an iterator
    try:
        prev, current = input.next(), input.next()
    except StopIteration:
        return
    for next in input:
        yield prev, current, next
        prev, current = current, next

def find_prev_next(objects, foo):
    prev, next = 0, 0
    for temp_prev, current, temp_next in trios(objects):
        if current == foo:
            prev, next = temp_prev, temp_next
    return prev, next

print find_prev_next(range(10), 1)
print find_prev_next(range(10), 0)
print find_prev_next(range(10), 10)
print find_prev_next(range(0), 10)
print find_prev_next(range(1), 10)
print find_prev_next(range(2), 10)

Veuillez noter que le comportement des limites est que nous ne cherchons jamais "foo" dans le premier ou le dernier élément, contrairement à votre code. Encore une fois, la sémantique des limites est étrange ... et difficile à comprendre de votre code :)

2
moshez

Je ne sais pas comment cela ne s'est pas encore produit puisqu'il n'utilise que des fonctions intégrées et qu'il est facilement extensible à d'autres décalages:

values = [1, 2, 3, 4]
offsets = [None] + values[:-1], values, values[1:] + [None]
for value in list(Zip(*offsets)):
    print(value) # (previous, current, next)

(None, 1, 2)
(1, 2, 3)
(2, 3, 4)
(3, 4, None)
1
Eric Czech

en utilisant des expressions conditionnelles pour la concision pour python> = 2.5

def prenext(l,v) : 
   i=l.index(v)
   return l[i-1] if i>0 else None,l[i+1] if i<len(l)-1 else None


# example
x=range(10)
prenext(x,3)
>>> (2,4)
prenext(x,0)
>>> (None,2)
prenext(x,9)
>>> (8,None)
1
makapuf

Pour ceux qui recherchent une solution à ce problème et qui souhaitent également faire défiler les éléments, les options ci-dessous peuvent fonctionner

from collections import deque  

foo = ['A', 'B', 'C', 'D']

def prev_and_next(input_list):
    CURRENT = input_list
    PREV = deque(input_list)
    PREV.rotate(-1)
    PREV = list(PREV)
    NEXT = deque(input_list)
    NEXT.rotate(1)
    NEXT = list(NEXT)
    return Zip(PREV, CURRENT, NEXT)

for previous_, current_, next_ in prev_and_next(foo):
    print(previous_, current_, next)
1
skr47ch

En utilisant des générateurs, c'est assez simple:

signal = ['→Signal value←']
def pniter( iter, signal=signal ):
    iA = iB = signal
    for iC in iter:
        if iB is signal:
            iB = iC
            continue
        else:
            yield iA, iB, iC
        iA = iB
        iB = iC
    iC = signal
    yield iA, iB, iC

if __== '__main__':
    print('test 1:')
    for a, b, c in pniter( range( 10 )):
        print( a, b, c )
    print('\ntest 2:')
    for a, b, c in pniter([ 20, 30, 40, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('\ntest 3:')
    cam = { 1: 30, 2: 40, 10: 9, -5: 36 }
    for a, b, c in pniter( cam ):
        print( a, b, c )
    for a, b, c in pniter( cam ):
        print( a, a if a is signal else cam[ a ], b, b if b is signal else cam[ b ], c, c if c is signal else cam[ c ])
    print('\ntest 4:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('\ntest 5:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ], ['sig']):
        print( a, b, c )
    print('\ntest 6:')
    for a, b, c in pniter([ 20, ['→Signal value←'], None, '→Signal value←', 60, 70, 80 ], signal ):
        print( a, b, c )

Notez que les tests qui incluent None et la même valeur que la valeur du signal fonctionnent toujours, car la vérification de la valeur du signal utilise "is" et que le signal est une valeur que Python n'interne pas. Toute valeur de marqueur singleton peut cependant être utilisée comme signal, ce qui peut simplifier le code utilisateur dans certaines circonstances.

0
Victoria

Vous pouvez simplement utiliser index dans la liste pour trouver où se trouve somevalue et obtenir les versions précédente et suivante selon vos besoins:


def find_prev_next(elem, elements):
    previous, next = None, None
    index = elements.index(elem)
    if index > 0:
        previous = elements[index -1]
    if index < (len(elements)-1):
        next = elements[index +1]
    return previous, next


foo = 'three'
list = ['one','two','three', 'four', 'five']

previous, next = find_prev_next(foo, list)

print previous # should print 'two'
print next # should print 'four'

0
John Montgomery

Autant que je sache, cela devrait être assez rapide, mais je ne l'ai pas testé

def iterate_prv_nxt(my_list):
    prv, cur, nxt = None, iter(my_list), iter(my_list)
    next(nxt, None)

    while True:
        try:
            if prv:
                yield next(prv), next(cur), next(nxt, None)
            else:
                yield None, next(cur), next(nxt, None)
                prv = iter(my_list)
        except StopIteration:
            break

Exemple d'utilisation:

>>> my_list = ['a', 'b', 'c']
>>> for prv, cur, nxt in iterate_prv_nxt(my_list):
...    print prv, cur, nxt
... 
None a b
a b c
b c None
0
Sfisol