Tengo un método que llama a otros 4 métodos en secuencia para verificar condiciones específicas, y regresa de inmediato (sin verificar los siguientes) cada vez que uno devuelve algo Verdad.
def check_all_conditions():
x = check_size()
if x:
return x
x = check_color()
if x:
return x
x = check_tone()
if x:
return x
x = check_flavor()
if x:
return x
return None
Esto parece mucho código de equipaje. En lugar de cada instrucción if de 2 líneas, prefiero hacer algo como:
x and return x
Pero eso es inválido Python. ¿Me estoy perdiendo una solución simple y elegante aquí? Por cierto, en esta situación, esos cuatro métodos de verificación pueden ser costosos, por lo que no quiero llamarlos varias veces.
python
if-statement
Bernardo
fuente
fuente

x and return xes mejor queif x: return x? Este último es mucho más legible y, por lo tanto, mantenible. No debe preocuparse demasiado por la cantidad de caracteres o líneas; la legibilidad cuenta. De todos modos, son exactamente el mismo número de caracteres que no son espacios en blanco, y si realmente debe hacerlo,if x: return xfuncionará bien en una sola línea.x(en oposición abool(x)), por lo que tal como está, creo que es seguro asumir que las funciones de OP pueden devolver cualquier cosa, y él quiere lo primero que sea verdadero.Respuestas:
Podrías usar un bucle:
Esto tiene la ventaja adicional de que ahora puede hacer que el número de condiciones sea variable.
Puede usar
map()+filter()(las versiones de Python 3, use lasfuture_builtinsversiones de Python 2) para obtener el primer valor coincidente:pero si esto es más legible es discutible.
Otra opción es usar una expresión generadora:
fuente
anylugar del bucle.return any(condition() for condition in conditions)anytiene casi la misma implementación en su interior. Pero se ve mucho mejor,conditionsyarguments? Esto es en mi humilde opinión mucho peor que el código original, que toma alrededor de 10 segundos para analizar por mi analizador mental.return next((check for check in checks if check), None).Alternativamente a la buena respuesta de Martijn, podrías encadenar
or. Esto devolverá el primer valor de verdad, oNonesi no hay un valor de verdad:Manifestación:
fuente
\para poner cada cheque en su propia línea.\para extender la línea lógica. Use paréntesis donde sea posible; así quereturn (....)con líneas nuevas insertadas según sea necesario. Aún así, esa será una larga línea lógica.No lo cambies
Hay otras formas de hacerlo, como lo muestran las otras respuestas. Ninguno es tan claro como su código original.
fuente
:, porque consideroif x: return xque está bastante bien, y hace que la función se vea más compacta. Pero eso puede ser solo yo.orcomo lo hizo timgeb es un idioma apropiado y bien entendido. Muchos idiomas tienen esto; tal vez cuando se le llamaorelse, es aún más clara, pero incluso el viejo y simpleor(o||en otros idiomas) está destinado a ser entendida como la alternativa para probar si el primero "no funciona."oretiquetado como no pitónico o de ninguna manera ofuscado, pero de todos modos eso es una cuestión de opinión, como debería ser.Efectivamente, la misma respuesta que timgeb, pero podría usar paréntesis para un mejor formato:
fuente
De acuerdo con la ley de Curly , puede hacer que este código sea más legible dividiendo dos preocupaciones:
en dos funciones:
Esto evita:
... conservando un flujo lineal y fácil de leer.
Probablemente también pueda encontrar nombres de funciones aún mejores, de acuerdo con su circunstancia particular, que lo hacen aún más legible.
fuente
return Noneno es necesario, porque las funciones regresanNonepor defecto. Sin embargo, no hay nada de malo en regresarNoneexplícitamente, y me gusta que hayas elegido hacerlo.Esta es una variante del primer ejemplo de Martijns. También utiliza el estilo "colección de llamadas" para permitir cortocircuitos.
En lugar de un bucle, puede usar el incorporado
any.Tenga en cuenta que
anydevuelve un valor booleano, por lo que si necesita el valor de retorno exacto del cheque, esta solución no funcionará.anyno va a distinguir entre14,'red','sharp','spicy'como valores de retorno, todos ellos serán devueltos comoTrue.fuente
next(itertools.ifilter(None, (c() for c in conditions)))para obtener el valor real sin convertirlo en un valor booleano.anyRealmente cortocircuito?x = bar(); if x: return x;¿Has considerado simplemente escribir
if x: return xtodo en una línea?Esto no es menos repetitivo que lo que tenía, pero IMNSHO se lee bastante más suave.
fuente
Estoy bastante sorprendido de que nadie haya mencionado el incorporado
anyque está hecho para este propósito:Tenga en cuenta que aunque esta implementación es probablemente la más clara, evalúa todas las comprobaciones, incluso si la primera es
True.Si realmente necesita detenerse en la primera verificación fallida, considere usar
reducewhich está hecho para convertir una lista a un valor simple:En tu caso:
lambda a, f: a or f()es la función que verifica si el acumuladorao la verificación actualf()esTrue. Tenga en cuenta que siaes asíTrue,f()no será evaluado.checkscontiene funciones de verificación (elfelemento de la lambda)Falsees el valor inicial, de lo contrario no se realizaría ninguna comprobación y el resultado siempre seríaTrueanyyreduceson herramientas básicas para la programación funcional. ¡Te recomiendo encarecidamente que los entrenes y que tambiénmapsea increíble!fuente
anysolo funciona si los cheques realmente devuelven un valor booleano, literalmenteTrueoFalse, pero la pregunta no especifica eso. Debería usarreducepara devolver el valor real devuelto por el cheque. Además, es bastante fácil evitar evaluar todas las comprobaciones conanyun generador, por ejemploany(c() for c in (check_size, check_color, check_tone, check_flavor)). Como en la respuesta de Leonhardreduce. Al igual que @DavidZ, creo que su soluciónanydebería usar un generador y debe señalarse que se limita a regresarTrueoFalse.anyfunciona con valores verdaderos:any([1, "abc", False]) == Trueyany(["", 0]) == Falseanysolo funciona para ese propósito si los valores booleanos reales se devuelven de las funciones de verificación.Si desea la misma estructura de código, ¡podría usar declaraciones ternarias!
Creo que esto se ve bien y claro si lo miras.
Manifestación:
fuente
x if x else <something>solo se puede reducir ax or <something>Para mí, la mejor respuesta es la de @ phil-frost, seguida de @ wayne-werner.
Lo que me parece interesante es que nadie ha dicho nada sobre el hecho de que una función devolverá muchos tipos de datos diferentes, lo que hará que sea obligatorio hacer controles sobre el tipo de x para realizar cualquier trabajo adicional.
Entonces mezclaría la respuesta de @ PhilFrost con la idea de mantener un solo tipo:
Observe que
xse pasa como un argumento, pero tambiénall_conditionsse usa como un generador pasado de funciones de verificación donde todas ellas obtienen unxcontrol, y devuelvenTrueoFalse. Al usarfuncconall_conditionsel valor predeterminado, puede usarassessed_x(x), o puede pasar un generador personalizado adicional a través defunc.De esa manera, obtendrá
xtan pronto como pase un cheque, pero siempre será del mismo tipo.fuente
Idealmente, volvería a escribir las
check_funciones para devolverTrueo enFalselugar de un valor. Sus cheques luego se conviertenSuponiendo que su
xno es inmutable, su función aún puede modificarlo (aunque no pueden reasignarlo), pero una función llamadacheckrealmente no debería modificarlo de todos modos.fuente
Una ligera variación en el primer ejemplo de Martijns anterior, que evita el if dentro del bucle:
fuente
Status or c()omitirá / realizará un cortocircuito en las llamadas ac()if siStatuses verdad, por lo que el código en esta respuesta no parece llamar a más funciones que el código del OP. stackoverflow.com/questions/2580136/…Me gusta @ timgeb's. Mientras tanto, me gustaría agregar que no es necesario expresar
Noneen lareturndeclaración yaorque se evalúa la colección de declaraciones separadas y se devuelve el primer cero, ninguno vacío, ninguno, y si no hay ninguno,Nonese devuelve si hay unNoneo no!Entonces mi
check_all_conditions()función se ve así:Usando
timeitconnumber=10**7miré el tiempo de ejecución de una serie de sugerencias. En aras de la comparación, solo utilicé larandom.random()función para devolver una cadena o enNonefunción de números aleatorios. Aquí está el código completo:Y aquí están los resultados:
fuente
De esta manera está un poco fuera de la caja, pero creo que el resultado final es simple, legible y se ve bien.
La idea básica es
raiseuna excepción cuando una de las funciones se evalúa como verdadera y devuelve el resultado. Así es como podría verse:Necesitará una
assertFalseyfunción que genere una excepción cuando uno de los argumentos de la función llamada se evalúe como verdadero:Lo anterior podría modificarse para proporcionar también argumentos para las funciones que se evaluarán.
Y, por supuesto, necesitarás el
TruthyExceptionmismo. Esta excepción proporciona elobjectque desencadenó la excepción:Puede convertir la función original en algo más general, por supuesto:
Esto podría ser un poco más lento porque está utilizando una
ifdeclaración y manejando una excepción. Sin embargo, la excepción sólo se maneja un máximo de una hora, por lo que el éxito de rendimiento debe ser menor a menos que vaya a ejecutar la verificación y obtener unTruevalor de muchos miles de veces.fuente
StopIterationes un buen ejemplo: se genera una excepción cada vez que se agota un iterable. Lo que desea evitar es aumentar sucesivamente las excepciones una y otra vez, lo que sería costoso. Pero hacerlo una vez no lo es.La forma pitónica es usar reduce (como alguien ya mencionó) o itertools (como se muestra a continuación), pero me parece que simplemente usar un cortocircuito del
oroperador produce un código más clarofuente
Voy a saltar aquí y nunca he escrito una sola línea de Python, pero supongo que
if x = check_something(): return xes válido.si es así:
fuente
if ( x := check_size() ) :para el mismo efecto.O use
max:fuente
He visto algunas implementaciones interesantes de declaraciones de cambio / caso con dictos en el pasado que me llevaron a esta respuesta. Usando el ejemplo que ha proporcionado, obtendrá lo siguiente. (Es una locura
using_complete_sentences_for_function_names, porcheck_all_conditionslo que cambia su nombre astatus. Ver (1))La función de selección elimina la necesidad de llamar cada
check_FUNCTIONdos veces, es decir, evitacheck_FUNCTION() if check_FUNCTION() else nextagregar otra capa de función. Esto es útil para funciones de larga duración. Las lambdas en el dict retrasan la ejecución de sus valores hasta el ciclo while.Como beneficio adicional, puede modificar el orden de ejecución e incluso omitir algunas de las pruebas alterando
ky,spor ejemplo,k='c',s={'c':'b','b':None}reducir el número de pruebas e invertir el orden de procesamiento original.Los
timeitbecarios podrían regatear el costo de agregar una o dos capas adicionales a la pila y el costo de la búsqueda de datos, pero parece más preocupado por la belleza del código.Alternativamente, una implementación más simple podría ser la siguiente:
fuente
check_no/some/even/prime/every_third/fancy_conditionspero solo esta función, entonces ¿por qué no llamarlostatuso si uno insistecheck_status? Usar_all_es superfluo, no está asegurando la integridad de los universos. La nomenclatura seguramente debería usar un conjunto consistente de palabras clave que aprovechen el espaciado de nombres siempre que sea posible. Las oraciones largas sirven mejor como cadenas de documentos. Raramente se necesitan más de 8-10 caracteres para describir algo sucintamente.check_all_conditionses un mal nombre, porque es no comprobación de todas las condiciones, si una es verdadera. Yo usaría algo asímatches_any_condition.