web-dev-qa-db-fra.com

Boucle "pour" première itération

Salutations pyc-sires et py-ladies, J'aimerais savoir s'il existe un moyen élégant pour Pythonic d'exécuter une fonction sur la première itération de la boucle.

first = True
for member in something.get():
    if first:
        root.copy(member)
        first = False
    else:
        somewhereElse.copy(member)
    foo(member)
52
Rince

Vous avez plusieurs choix pour le modèle Head-Tail design.

seq= something.get()
root.copy( seq[0] )
foo( seq[0] )
for member in seq[1:]:
    somewhereElse.copy(member)
    foo( member )

Ou ca

seq_iter= iter( something.get() )
head = seq_iter.next()
root.copy( head )
foo( head )
for member in seq_iter:
    somewhereElse.copy( member )
    foo( member )

Les gens se plaignent du fait que ce n'est en quelque sorte pas "DRY" car le code "redundant foo (member)" C'est une prétention ridicule. Si cela était vrai, toutes les fonctions ne pourraient être utilisées qu'une seule fois. Quel est l'intérêt de définir une fonction si vous ne pouvez avoir qu'une seule référence?

31
S.Lott

Quelque chose comme ça devrait marcher.

for i, member in enumerate(something.get()):
    if i == 0:
         # Do thing
    # Code for everything

Cependant, je vous recommande fortement de réfléchir à votre code pour voir si vous devez vraiment le faire de cette façon, car c'est un peu "sale". Mieux vaut aller chercher l'élément qui nécessite une manipulation spéciale à l'avance, puis une manipulation régulière pour tous les autres éléments de la boucle.

La seule raison pour laquelle je vois que je ne le ferais pas de cette façon est pour une grande liste que vous obtiendriez d'une expression génératrice (que vous ne voudriez pas extraire à l'avance car elle ne tiendrait pas en mémoire), ou des situations similaires .

58
Daniel Bruce

que diriez-vous:

my_array = something.get()
for member in my_array:
    if my_array.index(member) == 0:
        root.copy(member)
    else:
        somewhereElse.copy(member)
    foo(member)

ou peut-être:

for index, member in enumerate(something.get()):
    if index == 0:
        root.copy(member)
    else:
        somewhereElse.copy(member)
    foo(member)

Documentation de méthode-index .

11
dummy

Je pense que c'est assez élégant, mais peut-être trop compliqué pour ce qu'il fait ...

from itertools import chain, repeat, izip
for place, member in izip(chain([root], repeat(somewhereElse)), something.get()):
    place.copy(member)
    foo(member)
6
fortran

Ici, je pourrais venir avec un idiome Pythonic qui peut avoir l’air "pertty". Bien que, très probablement, j'utiliserais le formulaire que vous avez suggéré en posant la question, juste pour que le code reste plus évident, bien que moins élégant.

def copy_iter():
    yield root.copy
    while True:
        yield somewhereElse.copy

for member, copy in Zip(something.get(), copy_iter()):
    copy(member)
    foo(member)

(désolé - la première fois que j'ai posté, avant la modification, le formulaire ne fonctionnerait pas, j'avais oublié d'obtenir un itérateur pour l'objet 'copie')

4
jsbueno

Cela marche:

for number, member in enumerate(something.get()):
    if not number:
        root.copy(member)
    else:
        somewhereElse.copy(member)
    foo(member)

Dans la plupart des cas, cependant, je suggérerais simplement de parcourir le whatever[1:] et de faire la chose en dehors de la boucle; c'est généralement plus lisible. Cela dépend de votre cas d'utilisation, bien sûr.

4
balpha

Que diriez-vous d'utiliser iter et de consommer le premier élément?

Édition: Pour revenir à la question du PO, vous devez exécuter une opération commune sur tous les éléments, puis une opération sur le premier élément et une autre sur le reste.

S'il ne s'agit que d'un seul appel de fonction, je dirais qu'il suffit de l'écrire deux fois. Cela ne mettra pas fin au monde. Si c'est plus compliqué, vous pouvez utiliser un décorateur pour encapsuler votre "première" fonction et votre "repos" avec une opération commune.

def common(item):
    print "common (x**2):", item**2

def wrap_common(func):
    """Wraps `func` with a common operation"""
    def wrapped(item):
        func(item)
        common(item)
    return wrapped

@wrap_common
def first(item):
    """Performed on first item"""
    print "first:", item+2

@wrap_common
def rest(item):
    """Performed on rest of items"""
    print "rest:", item+5

items = iter(range(5))
first(items.next())

for item in items:
    rest(item)

Sortie:

first: 2
common (x**2): 0
rest: 6
common (x**2): 1
rest: 7
common (x**2): 4
rest: 8
common (x**2): 9
rest: 9
common (x**2): 16

ou vous pourriez faire une tranche:

first(items[0])
for item in items[1:]:
    rest(item)
3
Ryan Ginstrom

Si quelque chose.get () itère sur quelque chose, vous pouvez le faire aussi comme suit:

root.copy(something.get())

for member in something.get():
  #  the rest of the loop
3
Eli Bendersky

Je pense que la première solution S.Lott est la meilleure, mais il y a un autre choix si vous utilisez un python assez récent (> = 2.6 je pense, car izip_longest ne semble pas disponible avant cette version) qui permet de faire des choses différentes pour le premier et successif, et peut être facilement modifié pour faire des opérations distinctes pour 1er, 2ème, 3ème élément ... ainsi.

from itertools import izip_longest

seq = [1, 2, 3, 4, 5]

def headfunc(value):
    # do something
    print "1st value: %s" % value

def tailfunc(value):
    # do something else
    print "this is another value: %s" % value

def foo(value):
    print "perform this at ANY iteration."

for member, func in izip_longest(seq, [headfunc], fillvalue=tailfunc):
    func(member)
    foo(member)
2
Alan Franzoni

Je ne connais pas Python, mais j'utilise presque le modèle exact de votre exemple.
Ce que je fais aussi est de rendre la condition if la plus fréquente, donc vérifiez habituellement if( first == false )
Pourquoi? pour les longues boucles, le premier ne sera vrai qu'une fois et les autres fois, ce qui signifie que, dans toutes les boucles sauf la première, le programme vérifiera la condition et passera à la partie else.
En vérifiant que tout d'abord est faux, il n'y aura qu'un seul saut vers la partie else. Je ne sais pas vraiment si cela ajoute de l'efficacité, mais je le fais quand même, juste pour être en paix avec mon nerd intérieur. 

PS: Oui, je sais qu’en entrant dans la partie if, il faut aussi sauter par-dessus l’autre pour continuer l’exécution, alors ma façon de faire est probablement inutile, mais c’est agréable. :RÉ

1
Petruza

Ne pouvez-vous pas faire root.copy(something.get()) avant la boucle?

EDIT: Désolé, j'ai manqué le deuxième bit. Mais vous avez l'idée générale. Sinon, énumérer et rechercher 0?

EDIT2: Ok, débarrassé de la deuxième idée idiote.

1
Skilldrick

Votre question est contradictoire. Vous dites "ne faites que quelque chose à la première itération", alors qu'en réalité, vous faites quelque chose de différent lors des premières itérations ou des suivantes. Voici comment je le tenterais:

copyfn = root.copy
for member in something.get():
    copyfn(member)
    foo(member)
    copyfn = somewhereElse.copy
0
aaa90210