83 votes

Une façon efficace de faire en sorte qu'une fonction ne s'exécute qu'une fois dans une boucle.

En ce moment, je fais des choses comme les suivantes, ce qui devient fastidieux :

run_once = 0
while 1:
    if run_once == 0:
        myFunction()
        run_once = 1:

Je suppose qu'il y a une manière plus acceptée de gérer ce genre de choses ?

Ce que je recherche, c'est qu'une fonction soit exécutée une fois, à la demande. Par exemple, en appuyant sur un certain bouton. Il s'agit d'une application interactive qui comporte de nombreux interrupteurs contrôlés par l'utilisateur. Avoir une variable junk pour chaque interrupteur, juste pour savoir s'il a été exécuté ou non, semblait inefficace.

11 votes

Si vous devez exécuter une fois, pourquoi ne pas appeler la fonction -> maFonction() une seule fois ! !!. S'il vous plaît, regardez votre code, expliquez mieux l'intention ! !!

5 votes

Pourquoi ne pouvez-vous pas le laisser en dehors de la boucle ? Plus de contexte s'il vous plaît.

0 votes

@pyfunc Faites-moi savoir si l'édition du code transmet plus clairement le problème.

141voto

aaronasterling Points 25749

J'utiliserais un décorateur sur la fonction pour gérer le suivi du nombre d'exécutions.

def run_once(f):
    def wrapper(*args, **kwargs):
        if not wrapper.has_run:
            wrapper.has_run = True
            return f(*args, **kwargs)
    wrapper.has_run = False
    return wrapper

@run_once
def my_function(foo, bar):
    return foo+bar

Maintenant my_function ne sera exécuté qu'une seule fois. Les autres appels à cette fonction renverront None . Il suffit d'ajouter un else à la clause if si vous voulez qu'il renvoie autre chose. Dans votre exemple, il n'a pas besoin de renvoyer quoi que ce soit.

Si vous ne contrôlez pas la création de la fonction, ou si la fonction doit être utilisée normalement dans d'autres contextes, vous pouvez simplement appliquer le décorateur manuellement aussi.

action = run_once(my_function)
while 1:
    if predicate:
        action()

Cela laissera my_function disponibles pour d'autres usages.

Enfin, si vous n'avez besoin de l'exécuter qu'une fois, deux fois, alors vous pouvez simplement faire

action = run_once(my_function)
action() # run once the first time

action.has_run = False
action() # run once the second time

0 votes

Cela semble correspondre à ce que je recherche, mais je ne suis pas sûr de la syntaxe de ce que vous utilisez. À quoi sert le caractère @ ?

4 votes

@Marcus Ottosson Le @ char en fait un décorateur. Un commentaire est trop long pour expliquer ce que c'est. Ici, nous avons un lien . Quoi qu'il en soit, il est préférable d'utiliser la deuxième forme que j'ai présentée et de l'appliquer manuellement. Vous devriez quand même savoir ce qu'est un décorateur.

2 votes

Il est intéressant de noter que cela fonctionne également pour les méthodes des classes.

23voto

Jamie Bullock Points 1026

Une autre option consiste à définir le func_code objet de code pour que votre fonction soit un objet de code pour une fonction qui ne fait rien. Cela doit être fait à la fin du corps de votre fonction.

Par exemple :

def run_once():  
   # Code for something you only want to execute once
   run_once.func_code = (lambda:None).func_code

Aquí run_once.func_code = (lambda:None).func_code remplace le code exécutable de votre fonction par le code de lambda:None, de sorte que tous les appels ultérieurs à run_once() ne fera rien.

Cette technique est moins souple que l'approche par décorateur suggérée dans le document intitulé réponse acceptée mais elle peut être plus concise si vous n'avez qu'une seule fonction à exécuter une fois.

10 votes

Et sur Python 3, run_once.__code__ = (lambda: None).__code__ .

6voto

Rafe Kettler Points 29389

Exécuter la fonction avant la boucle. Exemple :

myFunction()
while True:
    # all the other code being executed in your loop

C'est la solution évidente. S'il y a plus qu'il n'y paraît, la solution peut être un peu plus compliquée.

6 votes

Cela ne marchera pas parce que le PO indique "Par exemple, en appuyant sur un certain bouton. Il s'agit d'une application interactive qui comporte de nombreux interrupteurs contrôlés par l'utilisateur." Donc le PO veut que la fonction soit exécutable une fois à l'intérieur de la boucle d'exécution principale.

6voto

Ryan Ginstrom Points 8354

Je suppose qu'il s'agit d'une action qui ne doit être exécutée qu'une seule fois, si certaines conditions sont remplies. Puisque vous n'exécuterez pas toujours l'action, vous ne pouvez pas la faire sans condition en dehors de la boucle. C'est un peu comme si vous récupériez paresseusement certaines données (et les mettiez en cache) si vous recevez une demande, mais ne les récupériez pas autrement.

def do_something():
    [x() for x in expensive_operations]
    global action
    action = lambda : None

action = do_something
while True:
    # some sort of complex logic...
    if foo:
        action()

1 votes

Cela suppose que vous souhaitiez (ou puissiez) modifier la logique de do_something() ou myFunction() pour l'adapter à ce cas particulier. Peut-être que do_something/myFunction est appelé ailleurs dans un contexte différent. (Bien sûr, nous ne le saurons pas tant que le poster n'aura pas clarifié).

3 votes

C'est trop alambiqué. Mais c'est un bon truc. Mon principal reproche, et je crois qu'il est sérieux, est que do_something doit savoir qu'il va être aliasé à action ce qui me semble violer une sorte de séparation des préoccupations. Mais je le répète, c'est un bon truc.

3 votes

-1. Cela est désordonné et, dans les grandes bases de code, peut nuire à la lisibilité. Les variables globales, les nom action Une fonction est appelée à chaque fois (contrairement à la question originale où elle n'est appelée qu'une fois).

5voto

tzot Points 32224

Il existe de nombreuses façons de faire ce que vous voulez ; cependant, notez qu'il est tout à fait possible que - comme décrit dans la question - vous n'ayez pas à appeler la fonction à l'intérieur de la boucle.

Si vous insistez pour que l'appel de la fonction se fasse à l'intérieur de la boucle, vous pouvez également le faire :

needs_to_run= expensive_function
while 1:
    …
    if needs_to_run: needs_to_run(); needs_to_run= None
    …

0 votes

+1 Je suis tenté de supprimer ma réponse mais je l'ai un peu trop fait ces derniers temps.

0 votes

@aaron : vous ne devriez absolument pas supprimer votre réponse, quel que soit le rapport entre vos réponses supprimées / restantes :)

1 votes

Explicite if needs_to_run is not None et un formatage approprié pourrait être approprié ici. needs_to_run, run_once = None, needs_to_run; run_once() pourrait protéger des exceptions, rendre moins probables les invocations multiples en réponse aux frappes de l'utilisateur.

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