Comment quitter une clause if


104

Quelles sortes de méthodes existent pour sortir prématurément d'une ifclause?

Il y a des moments où j'écris du code et que je veux mettre une breakinstruction à l'intérieur d'une ifclause, seulement pour me rappeler que ceux-ci ne peuvent être utilisés que pour les boucles.

Prenons le code suivant comme exemple:

if some_condition:
   ...
   if condition_a:
       # do something
       # and then exit the outer if block
   ...
   if condition_b:
       # do something
       # and then exit the outer if block
   # more code here

Je peux penser à une façon de faire cela: en supposant que les cas de sortie se produisent dans des instructions if imbriquées, enveloppez le code restant dans un gros bloc else. Exemple:

if some_condition:
   ...
   if condition_a:
       # do something
       # and then exit the outer if block
   else:
       ...
       if condition_b:
           # do something
           # and then exit the outer if block
       else:
           # more code here

Le problème avec ceci est que plus d'emplacements de sortie signifie plus de code d'imbrication / indenté.

Alternativement, je pourrais écrire mon code pour que les ifclauses soient aussi petites que possible et ne nécessitent aucune sortie.

Quelqu'un connaît-il une bonne / meilleure façon de sortir d'une ifclause?

S'il y a des clauses else-if et else associées, je suppose que quitter les ignorerait.


2
Pour votre deuxième exemple de code - le savez-vous elif?
Craig McQueen

2
"Alternativement, je pourrais écrire mon code pour que les clauses if soient aussi petites que possible et ne nécessitent aucune sortie." - et ce serait sûrement la meilleure solution. :-)
Michał Marczyk

2
@Craig McQueen: Oui, mais disons que je voulais que le code s'exécute entre les instructions de condition? Par exemple, if a: #stuff; #stuff_inbetween; if b: #stuff;le code intermédiaire dépend de not amais ne dépend pas de b.
Roman

bonjour s'il vous plaît n'oubliez pas elif stackoverflow.com/a/2069680/7045119
kerbrose

Réponses:


99

(Cette méthode fonctionne pour les ifs, plusieurs boucles imbriquées et d'autres constructions que vous ne pouvez pas breakfacilement.)

Enveloppez le code dans sa propre fonction. Au lieu de break, utilisez return.

Exemple:

def some_function():
    if condition_a:
        # do something and return early
        ...
        return
    ...
    if condition_b:
        # do something else and return early
        ...
        return
    ...
    return

if outer_condition:
    ...
    some_function()
    ...

4
Je suis heureux d'ajouter à votre sac d'astuces de programmeur. D'après mon expérience, cette approche fonctionne presque chaque fois que vous êtes tenté d'utiliser un goto progressif. (Et cela fait à la fois allusion et aborde des situations où une seule fonction devient trop grande)
Drew Dormann

2
Idéalement, vous pouvez réaliser les deux, mais il y a des moments où vous devez échanger un bon code contre de bonnes performances. Ces moments sont rares, surtout lorsque vous envisagez d'utiliser Python. En d'autres termes: ne vous inquiétez pas trop de la surcharge des appels de fonction.
éphémère le

17
Il y a une vieille anecdote: «Dennis Ritchie a encouragé la modularité en disant à tout le monde que les appels de fonction étaient vraiment, vraiment bon marché en C. Tout le monde a commencé à écrire de petites fonctions et à modulariser. Des années plus tard, nous avons découvert que les appels de fonction étaient encore chers sur le PDP-11 , et le code VAX passait souvent 50% de son temps dans l'instruction CALLS. Dennis nous avait menti! Mais il était trop tard; nous étions tous accro ... "
éphémère

1
@ephemient: C'est drôle, y a-t-il plus à l'histoire? J'aimerais tout lire.
Roman

4
Cette citation est tirée du chapitre 4 du livre The Art of Unix Programming (en ligne sur faqs.org/docs/artu ). Vous devriez vraiment tout lire, si vous ne l'avez pas encore fait.
éphémère

55
de goto import goto, label

si une_condition:
   ...
   si condition_a:
       # faire quelque chose
       # puis quittez le bloc if externe
       aller .end
   ...
   si condition_b:
       # faire quelque chose
       # puis quittez le bloc if externe
       aller .end
   # plus de code ici

label .end

(Ne l'utilisez pas vraiment, s'il vous plaît.)


35
+1 parce que c'est drôle. Une recherche sur Google m'a révélé qu'il s'agissait d'un module de blague du poisson d'avril.
Roman

2
J'y ai lié aussi. Cliquez sur le premier goto.
éphémère du

1
cela me rappelle le code d'assemblage avec toutes sortes de branchements :)
phunehehe

2
@ephemient: Ah, je n'ai pas remarqué le lien. Je pensais que c'était la mise en évidence du code. Mais maintenant que je regarde votre code, je ne vois pas de véritable mise en évidence en cours ..
Roman

1
Lorsque PHP a introduit goto, je me suis tourné vers Python php.net/manual/en/control-structures.goto.php
Marc

25
while some_condition:
   ...
   if condition_a:
       # do something
       break
   ...
   if condition_b:
       # do something
       break
   # more code here
   break

3
Oh, j'aime vraiment cette idée. Je pense que cela résout exactement mon désir initial. Cependant, j'ai le sentiment tenace que ce n'est pas une bonne pratique (et pour cette raison, je vais conserver la réponse actuellement acceptée car elle favorise un bon style de codage).
Roman

6
Notez que vous pouvez conserver l'original si et emballer le tout dans un fichier while True:. Assurez-vous simplement de mettre une breakdéclaration à la fin! Pour les langages avec la construction do-while, il est plus idomatique de faire:do { code that can conditionally break out } while (false);
Thomas Eding

10

Vous pouvez émuler les fonctionnalités de goto avec des exceptions:

try:
    # blah, blah ...
    # raise MyFunkyException as soon as you want out
except MyFunkyException:
    pass

Clause de non-responsabilité: Je veux seulement attirer votre attention sur la possibilité de faire les choses de cette manière, alors que je ne le considère en aucun cas comme raisonnable dans des circonstances normales. Comme je l'ai mentionné dans un commentaire sur la question, structurer le code de manière à éviter les conditionnelles byzantines en premier lieu est de loin préférable. :-)


Haha, j'aime cette solution créative. Cependant, je respecterai votre clause de non-responsabilité et n'utiliserai pas un code aussi funky.
Roman

@Roman: Heureux d'ajouter une autre astuce aux côtés de Shmoopty - même si je me sens méchant en comparaison. ;-)
Michał Marczyk

8

Peut être ça?

if some_condition and condition_a:
       # do something
elif some_condition and condition_b:
           # do something
           # and then exit the outer if block
elif some_condition and not condition_b:
           # more code here
else:
     #blah
if

1
Ouais, ça pourrait marcher. Je pense que mon esprit s'est en quelque sorte vidé elifpendant que j'écrivais ceci. Bien que je pense que cela ne fonctionnerait pas dans une situation où je veux que le code s'exécute entre les instructions if imbriquées.
Roman

c'est en fait la bonne réponse. pourquoi je ne vois pas les gens recommander cela ?? :)
kerbrose

6

Pour ce qui a été réellement demandé, mon approche est de mettre ces ifs dans une boucle en une seule boucle

while (True):
    if (some_condition):
        ...
        if (condition_a):
            # do something
            # and then exit the outer if block
            break
        ...
        if (condition_b):
            # do something
            # and then exit the outer if block
            break
        # more code here
    # make sure it is looped once
    break

Essaye-le:

conditions = [True,False]
some_condition = True

for condition_a in conditions:
    for condition_b in conditions:
        print("\n")
        print("with condition_a", condition_a)
        print("with condition_b", condition_b)
        while (True):
            if (some_condition):
                print("checkpoint 1")
                if (condition_a):
                    # do something
                    # and then exit the outer if block
                    print("checkpoint 2")
                    break
                print ("checkpoint 3")
                if (condition_b):
                    # do something
                    # and then exit the outer if block
                    print("checkpoint 4")
                    break
                print ("checkpoint 5")
                # more code here
            # make sure it is looped once
            break

1
Pour être honnête, c'est la solution la plus propre. C'est très clair quand le code doit se fermer - vous n'avez pas à vous soucier de la portée des fonctions à l'intérieur des fonctions, et il n'y a aucune performance ou dette logique.
Trent

2
Pourrait envisager d'utiliser for _ in range(1):au lieu de while True:. (1) Mieux communiquer votre intention d'une boucle d'itération unique et (2) pas de dernière instruction break pour quitter la boucle (pourrait être supprimée par accident plus tard)
Bernhard Kausler

3

En général, ne le faites pas. Si vous imbriquez des «si» et que vous vous en débarrassez, vous le faites mal.

Cependant, si vous devez:

if condition_a:
   def condition_a_fun():
       do_stuff()
       if we_wanna_escape:
           return
   condition_a_fun()
if condition_b:
   def condition_b_fun():
       do_more_stuff()
       if we_wanna_get_out_again:
           return
   condition_b_fun()

Notez que les fonctions NE DOIVENT pas être déclarées dans l'instruction if, elles peuvent être déclarées à l'avance;) Ce serait un meilleur choix, car cela évitera d'avoir à refactoriser un si / puis laid plus tard.


Merci d'avoir répondu. Je ne sais pas ce que vous entendez par «boucles d'imbrication». Si vous faites référence à ma mention du mot-clé «break», j'essayais simplement de motiver ma recherche d'une sortie if en la comparant à l'existence d'une sortie de boucle. De plus, je ne sais pas comment votre code résout le problème, comme mon exemple l'avait fait if condition_aet if condition_bimbriqué dans un fichier if some_condition. Je veux pouvoir sortir du if some_condition.
Roman

La raison pour laquelle les gens veulent imbriquer les ifs et les casser n'est probablement pas parce qu'ils le font mal, mais peut-être parce qu'ils veulent écrire du code propre, simple et DRY. Le cas simple est celui où le programme a besoin de faire x () lorsque condition_a et condition_b sont remplies, et doit faire y () uniquement pour condition_a, et doit faire z () uniquement pour condition_b. et le codeur refuse d'écrire x () plusieurs fois
izzulmakin

1

En fait, ce que vous décrivez sont des déclarations goto, qui sont généralement assez fortement analysées. Votre deuxième exemple est beaucoup plus facile à comprendre.

Cependant, plus propre serait toujours:

if some_condition:
   ...
   if condition_a:
       your_function1()
   else:
       your_function2()

...

def your_function2():
   if condition_b:
       # do something
       # and then exit the outer if block
   else:
       # more code here

1

Il existe un autre moyen qui ne repose pas sur la définition de fonctions (car parfois c'est moins lisible pour les petits extraits de code), n'utilise pas une boucle while externe supplémentaire (qui peut nécessiter une appréciation particulière dans les commentaires pour être même compréhensible à première vue) , n'utilise pas goto (...) et surtout vous permet de garder votre niveau d'indentation pour l'extérieur si c'est le cas, vous n'avez pas à commencer à imbriquer des choses.

if some_condition:
   ...
   if condition_a:
       # do something
       exit_if=True # and then exit the outer if block
if some condition and not exit_if: # if and only if exit_if wasn't set we want to execute the following code
   # keep doing something
   if condition_b:
       # do something
       exit_if=True # and then exit the outer if block
if some condition and not exit_if:
   # keep doing something

Oui, cela nécessite également un deuxième regard pour la lisibilité, cependant, si les extraits de code sont petits, cela ne nécessite pas de suivre des boucles while qui ne se répéteront jamais et après avoir compris à quoi servent les ifs intermédiaires, il est facilement lisible, tout en un endroit et avec la même indentation.

Et cela devrait être assez efficace.


0

Alors là, je comprends que vous essayez de sortir du bloc de code externe si

if some_condition:
    ...
    if condition_a:
       # do something
       # and then exit the outer if block
       ...
    if condition_b:
       # do something
       # and then exit the outer if block
# more code here

Une façon de s'en sortir est que vous pouvez tester une condition fausse dans le bloc if externe, qui sortira ensuite implicitement du bloc de code, vous utiliserez ensuite un bloc else pour imbriquer les autres ifs pour faire quelque chose

if test_for_false:
    # Exit the code(which is the outer if code)

else:
    if condition_a:
        # Do something

    if condition_b:
        # Do something

0

La seule chose qui appliquerait cela sans méthodes supplémentaires est elifl'exemple suivant

a = ['yearly', 'monthly', 'quartly', 'semiannual', 'monthly', 'quartly', 'semiannual', 'yearly']
# start the condition
if 'monthly' in b: 
    print('monthly') 
elif 'quartly' in b: 
    print('quartly') 
elif 'semiannual' in b: 
    print('semiannual') 
elif 'yearly' in b: 
    print('yearly') 
else: 
    print('final') 

-1

Voici une autre façon de gérer cela. Il utilise un seul élément pour la boucle qui vous permet d'utiliser simplement continuer. Cela évite le besoin inutile d'avoir des fonctions supplémentaires sans raison. Et élimine en outre les boucles while potentielles infinies.

if something:
    for _ in [0]:
        # Get x
        if not x:
            continue

        # Get y
        if not y:
            continue

        # Get z
        if not z:
            continue

        # Stuff that depends on x, y, and z

-2

utiliser returndans la condition if vous fera sortir de la fonction, de sorte que vous puissiez utiliser return pour rompre la condition if.


2
il veut casser le sinon quitter la fonction
StealthOne
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.