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)
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?
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 .
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 .
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)
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')
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.
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)
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
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)
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É
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.
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