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 x
es 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 x
funcionará 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_builtins
versiones 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
any
lugar del bucle.return any(condition() for condition in conditions)
any
tiene casi la misma implementación en su interior. Pero se ve mucho mejor,conditions
yarguments
? 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, oNone
si 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 x
que está bastante bien, y hace que la función se vea más compacta. Pero eso puede ser solo yo.or
como 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."or
etiquetado 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 None
no es necesario, porque las funciones regresanNone
por defecto. Sin embargo, no hay nada de malo en regresarNone
explí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
any
devuelve un valor booleano, por lo que si necesita el valor de retorno exacto del cheque, esta solución no funcionará.any
no 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.any
Realmente cortocircuito?x = bar(); if x: return x;
¿Has considerado simplemente escribir
if x: return x
todo 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
any
que 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
reduce
which 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 acumuladora
o la verificación actualf()
esTrue
. Tenga en cuenta que sia
es asíTrue
,f()
no será evaluado.checks
contiene funciones de verificación (elf
elemento de la lambda)False
es el valor inicial, de lo contrario no se realizaría ninguna comprobación y el resultado siempre seríaTrue
any
yreduce
son herramientas básicas para la programación funcional. ¡Te recomiendo encarecidamente que los entrenes y que tambiénmap
sea increíble!fuente
any
solo funciona si los cheques realmente devuelven un valor booleano, literalmenteTrue
oFalse
, pero la pregunta no especifica eso. Debería usarreduce
para devolver el valor real devuelto por el cheque. Además, es bastante fácil evitar evaluar todas las comprobaciones conany
un 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ónany
debería usar un generador y debe señalarse que se limita a regresarTrue
oFalse
.any
funciona con valores verdaderos:any([1, "abc", False]) == True
yany(["", 0]) == False
any
solo 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
x
se pasa como un argumento, pero tambiénall_conditions
se usa como un generador pasado de funciones de verificación donde todas ellas obtienen unx
control, y devuelvenTrue
oFalse
. Al usarfunc
conall_conditions
el valor predeterminado, puede usarassessed_x(x)
, o puede pasar un generador personalizado adicional a través defunc
.De esa manera, obtendrá
x
tan pronto como pase un cheque, pero siempre será del mismo tipo.fuente
Idealmente, volvería a escribir las
check_
funciones para devolverTrue
o enFalse
lugar de un valor. Sus cheques luego se conviertenSuponiendo que su
x
no es inmutable, su función aún puede modificarlo (aunque no pueden reasignarlo), pero una función llamadacheck
realmente 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 siStatus
es 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
None
en lareturn
declaración yaor
que se evalúa la colección de declaraciones separadas y se devuelve el primer cero, ninguno vacío, ninguno, y si no hay ninguno,None
se devuelve si hay unNone
o no!Entonces mi
check_all_conditions()
función se ve así:Usando
timeit
connumber=10**7
miré 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 enNone
funció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
raise
una excepción cuando una de las funciones se evalúa como verdadera y devuelve el resultado. Así es como podría verse:Necesitará una
assertFalsey
funció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
TruthyException
mismo. Esta excepción proporciona elobject
que 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
if
declaració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 unTrue
valor de muchos miles de veces.fuente
StopIteration
es 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
or
operador 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 x
es 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_conditions
lo que cambia su nombre astatus
. Ver (1))La función de selección elimina la necesidad de llamar cada
check_FUNCTION
dos veces, es decir, evitacheck_FUNCTION() if check_FUNCTION() else next
agregar 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
k
y,s
por ejemplo,k='c',s={'c':'b','b':None}
reducir el número de pruebas e invertir el orden de procesamiento original.Los
timeit
becarios 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_conditions
pero solo esta función, entonces ¿por qué no llamarlostatus
o 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_conditions
es un mal nombre, porque es no comprobación de todas las condiciones, si una es verdadera. Yo usaría algo asímatches_any_condition
.