Cómo salir de una cláusula if

104

¿Qué tipo de métodos existen para salir prematuramente de una ifcláusula?

Hay momentos en los que estoy escribiendo código y quiero poner una breakdeclaración dentro de una ifcláusula, solo para recordar que solo se pueden usar para bucles.

Tomemos el siguiente código como ejemplo:

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

Puedo pensar en una forma de hacer esto: suponiendo que los casos de salida sucedan dentro de las declaraciones if anidadas, envuelva el código restante en un gran bloque else. Ejemplo:

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

El problema con esto es que más ubicaciones de salida significan más código anidado / sangrado.

Alternativamente, podría escribir mi código para que las ifcláusulas sean lo más pequeñas posible y no requieran ninguna salida.

¿Alguien sabe de una buena / mejor manera de salir de una ifcláusula?

Si hay cláusulas else-if y else asociadas, me imagino que al salir se omitirían.

romano
fuente
2
Para su segunda muestra de código, ¿conoce elif?
Craig McQueen
2
"Alternativamente, podría escribir mi código para que las cláusulas if sean lo más pequeñas posible y no requieran salidas". - y seguramente este sería el mejor curso de acción. :-)
Michał Marczyk
2
@Craig McQueen: Sí, ¿pero digo que quería que el código se ejecutara entre declaraciones de condición? Por ejemplo, if a: #stuff; #stuff_inbetween; if b: #stuff;el código intermedio depende de not apero no depende de b.
Roman
hola, por favor, no olvides elif stackoverflow.com/a/2069680/7045119
kerbrose

Respuestas:

99

(Este método funciona para ifs, múltiples bucles anidados y otras construcciones de las que no se puede breakfácilmente).

Envuelve el código en su propia función. En lugar de break, utilice return.

Ejemplo:

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()
    ...
Drew Dormann
fuente
4
Me complace agregar a su bolsa de trucos de programador. En mi experiencia, ese enfoque funciona casi cada vez que estás tentado a usar un goto que se mueve hacia adelante. (Y sugiere y aborda situaciones en las que una sola función se está volviendo demasiado grande)
Drew Dormann
2
Idealmente, puede lograr ambos, pero hay ocasiones en las que debe cambiar un buen código por un buen rendimiento. Esos momentos son raros, especialmente cuando está considerando usar Python. En otras palabras: no se preocupe tanto por la sobrecarga de llamadas a funciones.
ephemient
17
Hay una vieja anécdota: "Dennis Ritchie fomentó la modularidad diciéndoles a todos y cada uno que las llamadas a funciones eran realmente, muy baratas en C. Todos comenzaron a escribir funciones pequeñas y modularizar. Años más tarde descubrimos que las llamadas a funciones aún eran caras en el PDP-11 , y el código VAX a menudo pasaba el 50% de su tiempo en la instrucción CALLS. ¡Dennis nos había mentido! Pero era demasiado tarde; todos estábamos enganchados ... "
ephemient
1
@ephemient: Eso es gracioso, ¿hay más en la historia? Me gustaría leerlo todo.
Roman
4
Esta cita es del capítulo 4 del libro The Art of Unix Programming (en línea en faqs.org/docs/artu ). Deberías leerlo todo, si no lo has hecho antes.
ephemient
55
de ir a importar ir a, etiqueta

si alguna_condición:
   ...
   si condition_a:
       # hacer algo
       # y luego salir del bloque if externo
       ir a .end
   ...
   if condition_b:
       # hacer algo
       # y luego salir del bloque if externo
       ir a .end
   # más código aquí

etiqueta .end

(En realidad, no uses esto, por favor).

efímero
fuente
35
+1 porque esto es divertido. Una búsqueda en Google me reveló que este era un módulo de broma de April Fool.
Roman
2
Yo también lo vinculé. Haga clic en el primero goto.
ephemient
1
esto me recuerda al código ensamblador con todo tipo de ramificaciones :)
phunehehe
2
@ephemient: Ah, no noté el enlace. Pensé que era resaltado de código. Pero ahora que miro su código, no veo ningún resaltado real en marcha ..
Roman
1
Cuando PHP introdujo goto, recurrí
Marc
25
while some_condition:
   ...
   if condition_a:
       # do something
       break
   ...
   if condition_b:
       # do something
       break
   # more code here
   break
Thomas Eding
fuente
3
Oh, oye, realmente me gusta esta idea. Creo que esto resuelve exactamente mi deseo original. Sin embargo, tengo la persistente sensación de que esto no es una buena práctica (y por esa razón, retendré la respuesta aceptada actual por ahora porque promueve un buen estilo de codificación).
Roman
6
Tenga en cuenta que puede conservar el if original y envolver todo en formato while True:. ¡Solo asegúrate de poner una breakdeclaración al final! Para lenguajes con la construcción do-while, es más idomático hacer:do { code that can conditionally break out } while (false);
Thomas Eding
10

Puede emular la funcionalidad de goto con excepciones:

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

Descargo de responsabilidad: solo quiero llamar su atención sobre la posibilidad de hacer las cosas de esta manera, mientras que de ninguna manera lo apoyo como razonable en circunstancias normales. Como mencioné en un comentario sobre la pregunta, es preferible estructurar el código para evitar los condicionales bizantinos en primer lugar. :-)

Michał Marczyk
fuente
Jaja, me gusta esta solución creativa. Aunque cumpliré su descargo de responsabilidad y no usaré un código tan funky.
Roman
@Roman: Feliz de agregar otro truco junto al de Shmoopty, incluso cuando me siento travieso en comparación. ;-)
Michał Marczyk
8

¿tal vez esto?

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
ghostdog74
fuente
1
Sí, eso podría funcionar. Creo que mi mente se elifquedó en blanco mientras escribía esto. Aunque creo que esto no funcionaría en una situación en la que quiero que el código se ejecute entre las declaraciones if anidadas.
Roman
esta es realmente la respuesta correcta. ¿Por qué no veo que la gente recomiende esto? :)
kerbrose
6

Para lo que realmente se preguntó, mi enfoque es poner esos ifs dentro de un bucle de un bucle

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

Pruébalo:

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
izzulmakin
fuente
1
Para ser honesto, esta es la solución más limpia. Está muy claro cuándo va a salir el código: no tiene que preocuparse por el alcance de las funciones dentro de las funciones, y no hay rendimiento ni deuda lógica.
Trent
2
Podría considerar usar en for _ in range(1):lugar de while True:. (1) Comunique mejor su intención de un solo bucle de iteración y (2) ninguna declaración de última interrupción para salir del bucle (podría eliminarse por accidente más adelante)
Bernhard Kausler
3

En general, no lo hagas. Si está anidando "si" y rompiendo con ellos, lo está haciendo mal.

Sin embargo, si debe:

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()

Tenga en cuenta que las funciones no TIENEN que ser declaradas en la instrucción if, se pueden declarar por adelantado;) Esta sería una mejor opción, ya que evitará la necesidad de refactorizar un if / then feo más adelante.

Enki
fuente
Gracias por la respuesta. No estoy seguro de lo que quiere decir con "bucles de anidamiento". Si se refiere a mi mención de la palabra clave 'descanso', simplemente estaba tratando de motivar mi búsqueda de una salida if comparándola con la existencia de una salida de bucle. Además, no estoy seguro de cómo su código resuelve el problema, ya que mi ejemplo lo tenía if condition_ay estaba if condition_banidado dentro de un archivo if some_condition. Quiero poder salirme del if some_condition.
Roman
La razón por la que la gente quiere anidar ifs y romperlos probablemente no sea porque lo estén haciendo mal, sino quizás porque quieren escribir código limpio, simple y DRY. El caso simple es cuando el programa necesita hacer x () cuando se cumplen tanto condition_a como condition_b, y necesita hacer y () solo para condition_a, y necesita hacer z () solo para condition_b. y el codificador se niega a escribir x () varias veces
izzulmakin
1

Efectivamente, lo que está describiendo son declaraciones goto, que generalmente se analizan con bastante frecuencia. Su segundo ejemplo es mucho más fácil de entender.

Sin embargo, aún más limpio sería:

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
Smashery
fuente
1

Hay otra forma que no se basa en la definición de funciones (porque a veces es menos legible para fragmentos de código pequeños), no usa un bucle while externo adicional (que podría necesitar una apreciación especial en los comentarios para incluso ser comprensible a primera vista) , no usa goto (...) y lo más importante es que mantenga su nivel de sangría para el exterior si es así, no tiene que comenzar a anidar cosas.

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

Sí, eso también necesita una segunda mirada para la legibilidad, sin embargo, si los fragmentos de código son pequeños, no es necesario realizar un seguimiento de los bucles while que nunca se repetirán y, después de comprender para qué sirven los if intermedios, es fácilmente legible, todo en un lugar y con la misma sangría.

Y debería ser bastante eficiente.

DonQuiKong
fuente
0

Entonces, entiendo que estás tratando de salir del bloque de código if externo

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

Una forma de salir de esto es que puede probar una condición falsa en el bloque if externo, que luego saldrá implícitamente del bloque de código, luego use un bloque else para anidar los otros ifs para hacer algo

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

else:
    if condition_a:
        # Do something

    if condition_b:
        # Do something
Romeo
fuente
0

Lo único que aplicaría esto sin métodos adicionales es elifel siguiente ejemplo

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') 
kerbrose
fuente
-1

Aquí hay otra forma de manejar esto. Utiliza un solo elemento para bucle que le permite usar continuar. Evita la necesidad innecesaria de tener funciones adicionales sin ningún motivo. Y, además, elimina los posibles bucles while infinitos.

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
Timbot
fuente
-2

use returnen la condición if lo devolverá de la función, de modo que pueda usar return para romper la condición if.

Nikhil Parashar
fuente
2
quiere romper la función si no sale
StealthOne