2011-07-22 15 views
7

Estoy usando Python para mi ejemplo, pero mi pregunta se refiere a los lenguajes de programación en general.Uso del retorno en sentencias largas if-elseif-else (Python)

def some_function(eggs): 
    if eggs == 1: 
     do_something_1() 
    elif eggs == 2: 
     do_something_2() 
    elif eggs == 3: 
     do_something_3() 
    else: 
     do_error() 
     return 
    do_something_4() 
    do_something_5() 
    do_something_6() 

(Esto es sólo un ejemplo. Mis funciones no serán llamados do_something_x.)

¿Podría poner un retorno en el que se le parezca esto una mala práctica de programación? ¿Sería una mejor idea de poner

do_something_4() 
do_something_5() 
do_something_6() 

en cada una de las IF/Elifs?

Respuesta

11

El principal problema que veo con su código es que el caso de error se oculta más de la mitad del cuerpo de la función. Hace que el código sea difícil de leer. Como lo que está haciendo es validar los argumentos para la función, primero debe hacerlo.

Mi preferencia en el caso de un argumento no válido es presentar una excepción apropiada como ValueError. Sin saber lo que hace su función, o lo que hace do_error, es difícil decir con absoluta certeza que esto se aplica a su caso. Pero en general, obtener argumentos incorrectos no es algo de lo que una función pueda recuperarse. El que llama dio los argumentos; así que ponga la responsabilidad en la persona que llama para recuperarse de ese error.

Además, aquí es un lenguaje que puede utilizar para evitar largas listas de elif s:

funs = {1: do_something_1, 
     2: do_something_2, 
     3: do_something_3} 
funs[eggs]() 
+3

O incluso 'funs.get (eggs, do_error)()' para manejar el caso donde 'eggs' no está en' funs'. –

+0

Al validar los argumentos, ¿quiere decir 'si huevos! = 1 y huevos! = 2 y huevos! = 3'? Porque eso no es muy simple si tengo 100 opciones para el valor de los huevos. –

+0

Si usa el modismo que sugerí, mire el comentario de Andrew, o use 'if eggs not in funs: raise ValueError (" Número inválido de huevos ")' –

3

¿Qué tal:

def some_function(eggs): 
    if eggs not in [1,2,3]: 
     do_error() 
     return 

    if eggs == 1: 
     do_something_1() 
    elif eggs == 2: 
     do_something_2() 
    elif eggs == 3: 
     do_something_3() 
    else: 
     assert False 
    do_something_4() 
    do_something_5() 
    do_something_6() 
+2

Me gusta mucho esta respuesta, en realidad. Hay dos campamentos para: retornos anticipados: el campo "todas las funciones deben tener un solo punto de salida" y el campo "regresar cuando se puede limitar el procesamiento total". Tiendo a estar del lado de este último, especialmente con Python. Parece simplificar las funciones, reducir la indentación y "sentirse" más limpio. –

+5

No soy muy aficionado a la afirmación, ya que se usa aquí, ya tienes el invariante marcado explícitamente en la parte superior. – SingleNegationElimination

+0

El problema que veo con esto es que en realidad es un ejemplo de duplicación de código, el problema exacto que queremos evitar. Tener las opciones (1, 2, 3) enumeradas en varios lugares abre la posibilidad de una falta de coincidencia más tarde. ¿Qué pasa si el autor agrega una opción 4 pero no actualiza el "no en"? – dhg

2

¿Estás realmente seguro de que do_something_n está muy relacionado con do_something_m?

Si es así, use do_something(var, n) y use el mismo código para todo con unos pocos if (después de todo, el concepto está realmente relacionado, ¿no?).

De lo contrario, divida las funciones en funciones realmente útiles y autónomas.


Ejemplo qué esto (probablemente) es malo:

def print1(): 
    print(1) 

def print2(): 
    print(2) 

Bueno, alguien debería ver esto debería ser printn(n) o algo similar.

Y el otro probabilidad:

def action1(): 
    paymanagers() 
    payworkers() 

def action2(): 
    clean_trashbin() 
    unlock_car() 

Estas acciones están probablemente no relacionados y deben pertenecer en sus propias funciones.

1

Lo que está haciendo ahora no es una mala práctica de programación, pero sería una mala práctica duplicar el código al poner las tres llamadas de función en cada instrucción if.

Algunas personas prefieren tener un único punto de salida de sus funciones, en cuyo caso yo sugeriría algo como esto:

def some_function(eggs): 
    error_code = 0 
    if eggs == 1: 
     do_something_1() 
    elif eggs == 2: 
     do_something_2() 
    elif eggs == 3: 
     do_something_3() 
    else: 
     do_error() 
     error_code = 1 

    if error_code == 0: 
     do_something_4() 
     do_something_5() 
     do_something_6() 
    return # return error_code if it would be helpful to the calling function 
+3

Esas personas deberían usar otro lenguaje más funcional, si es tan importante para ellos. El regreso temprano es * mucho * más limpio, más legible y mantenible que el desglose del código con las variables de estilo 'error_code'. –

8

Definitivamente hacer no copia idéntica de código en cada cláusula if.

¿Qué tal:

def some_function(eggs): 
    options = {1: do_something_1, 2: do_something_2, 3: do_something_3} 
    if eggs in options: 
     options[eggs]() 
     do_something_4() 
     do_something_5() 
     do_something_6() 
    else: 
     do_error() 
     return 

Esto no requiere un largo ifelifelse. También está claro do_something_4() etc. solo ocurre si eggs es 1, 2 o 3.