while switch not else ejemplos def python if-statement

python - switch - Forma pitónica para evitar las declaraciones "if x: return x"



switch python (18)

¿Has considerado simplemente escribir if x: return x all en una línea?

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 no es menos repetitivo que lo que tenía, pero IMNSHO se lee un poco más suave.

Tengo un método que llama a otros 4 métodos en secuencia para verificar condiciones específicas, y regresa inmediatamente (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.


Alternativamente a la buena respuesta de Martijn, podría encadenar or . Esto devolverá el primer valor verdadero, o None si no hay un valor verdadero:

def check_all_conditions(): return check_size() or check_color() or check_tone() or check_flavor() or None

Manifestación:

>>> x = [] or 0 or {} or -1 or None >>> x -1 >>> x = [] or 0 or {} or '''' or None >>> x is None True


De acuerdo con la ley de Curly , puede hacer que este código sea más legible dividiendo dos preocupaciones:

  • ¿Qué cosas reviso?
  • ¿Ha vuelto una cosa verdad?

en dos funciones:

def all_conditions(): yield check_size() yield check_color() yield check_tone() yield check_flavor() def check_all_conditions(): for condition in all_conditions(): if condition: return condition return None

Esto evita:

  • estructuras lógicas complicadas
  • líneas muy largas
  • repetición

... 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, lo que lo hace aún más legible.


Efectivamente, la misma respuesta que timgeb, pero podría usar paréntesis para un mejor formato:

def check_all_the_things(): return ( one() or two() or five() or three() or None )


Esta es una variante del primer ejemplo de Martijns. También utiliza el estilo de "colección de llamadas" para permitir cortocircuitos.

En lugar de un bucle, puede usar el incorporado.

conditions = (check_size, check_color, check_tone, check_flavor) return any(condition() for condition in conditions)

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 distinguirá entre 14 , ''red'' , ''sharp'' , ''spicy'' como valores de retorno, todos serán devueltos como True .


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 devolver el resultado. Así es como podría verse:

def check_conditions(): try: assertFalsey( check_size, check_color, check_tone, check_flavor) except TruthyException as e: return e.trigger else: return None

Necesitará una función assertFalsey que assertFalsey una excepción cuando uno de los argumentos de la función llamada se evalúe como verdadero:

def assertFalsey(*funcs): for f in funcs: o = f() if o: raise TruthyException(o)

Lo anterior podría modificarse para proporcionar también argumentos para las funciones que se evaluarán.

Y, por supuesto, necesitará la TruthyException sí. Esta excepción proporciona el object que activó la excepción:

class TruthyException(Exception): def __init__(self, obj, *args): super().__init__(*args) self.trigger = obj

Puede convertir la función original en algo más general, por supuesto:

def get_truthy_condition(*conditions): try: assertFalsey(*conditions) except TruthyException as e: return e.trigger else: return None result = get_truthy_condition(check_size, check_color, check_tone, check_flavor)

Esto podría ser un poco más lento porque está utilizando una instrucción if y manejando una excepción. Sin embargo, la excepción solo se maneja un máximo de una vez, por lo que el impacto en el rendimiento debe ser menor a menos que espere ejecutar la verificación y obtener un valor True muchos miles de veces.


Estoy bastante sorprendido de que nadie mencione el incorporado que está hecho para este propósito:

def check_all_conditions(): return any([ check_size(), check_color(), check_tone(), check_flavor() ])

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 que se hace para convertir una lista a un valor simple:

def check_all_conditions(): checks = [check_size, check_color, check_tone, check_flavor] return reduce(lambda a, f: a or f(), checks, False)

reduce(function, iterable[, initializer]) : aplica la función de dos argumentos acumulativamente a los elementos de iterable, de izquierda a derecha, para reducir el iterable a un solo valor. El argumento izquierdo, x, es el valor acumulado y el argumento derecho, y, es el valor de actualización del iterable. Si el inicializador opcional está presente, se coloca antes de los elementos del iterable en el cálculo

En tu caso:

  • lambda a, f: a or f() es la función que verifica que el acumulador ao la verificación actual f() sea True . Tenga en cuenta que si a es True , f() no se evaluará.
  • checks contiene funciones de verificación (el elemento f de la lambda)
  • False es el valor inicial, de lo contrario no se realizaría ninguna comprobación y el resultado siempre sería True

any y reduce son herramientas básicas para la programación funcional. Te recomiendo encarecidamente que los entrenes y que hagas un map que también es increíble.


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 , así que check_all_conditions cambia su nombre a status . Ver (1))

def status(k = ''a'', s = {''a'':''b'',''b'':''c'',''c'':''d'',''d'':None}) : select = lambda next, test : test if test else next d = {''a'': lambda : select(s[''a''], check_size() ), ''b'': lambda : select(s[''b''], check_color() ), ''c'': lambda : select(s[''c''], check_tone() ), ''d'': lambda : select(s[''d''], check_flavor())} while k in d : k = d[k]() return k

La función select elimina la necesidad de llamar a cada check_FUNCTION dos veces, es decir, evita check_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} reduce el número de pruebas e invierte el orden de procesamiento original.

Los compañeros de tiempo pueden 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:

def status(k=check_size) : select = lambda next, test : test if test else next d = {check_size : lambda : select(check_color, check_size() ), check_color : lambda : select(check_tone, check_color() ), check_tone : lambda : select(check_flavor, check_tone() ), check_flavor: lambda : select(None, check_flavor())} while k in d : k = d[k]() return k

  1. Me refiero a esto no en términos de pep8 sino en términos de usar una palabra descriptiva concisa en lugar de una oración. Por supuesto, el OP puede estar siguiendo alguna convención de codificación, trabajando una base de código existente o no importa términos breves en su base de código.

Idealmente, volvería a escribir las funciones check_ para devolver True o False lugar de un valor. Sus cheques luego se convierten

if check_size(x): return x #etc

Suponiendo que su x no es inmutable, su función aún puede modificarla (aunque no pueden reasignarla), pero una función llamada check no debería modificarla de todos modos.


La forma pitónica es usar reducir (como alguien ya mencionó) o itertools (como se muestra a continuación), pero me parece que simplemente usar un cortocircuito del operador or produce un código más claro

from itertools import imap, dropwhile def check_all_conditions(): conditions = (check_size,/ check_color,/ check_tone,/ check_flavor) results_gen = dropwhile(lambda x:not x, imap(lambda check:check(), conditions)) try: return results_gen.next() except StopIteration: return None


Me gusta @ timgeb''s. Mientras tanto, me gustaría agregar que no es necesario expresar None en la declaración de return ya que se evalúa la recopilación de declaraciones separadas y se devuelve la primera no-cero, ninguna-vacía, ninguna-Ninguna y si no hay ninguna ¡entonces se devuelve None si hay None o no!

Entonces mi función check_all_conditions() ve así:

def check_all_conditions(): return check_size() or check_color() or check_tone() or check_flavor()

Usando timeit con number=10**7 timeit el tiempo de ejecución de una serie de sugerencias. En aras de la comparación, acabo de utilizar la función random.random() para devolver una cadena o None basada en números aleatorios. Aquí está el código completo:

import random import timeit def check_size(): if random.random() < 0.25: return "BIG" def check_color(): if random.random() < 0.25: return "RED" def check_tone(): if random.random() < 0.25: return "SOFT" def check_flavor(): if random.random() < 0.25: return "SWEET" def check_all_conditions_Bernard(): 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 def check_all_Martijn_Pieters(): conditions = (check_size, check_color, check_tone, check_flavor) for condition in conditions: result = condition() if result: return result def check_all_conditions_timgeb(): return check_size() or check_color() or check_tone() or check_flavor() or None def check_all_conditions_Reza(): return check_size() or check_color() or check_tone() or check_flavor() def check_all_conditions_Phinet(): x = check_size() x = x if x else check_color() x = x if x else check_tone() x = x if x else check_flavor() return x if x else None def all_conditions(): yield check_size() yield check_color() yield check_tone() yield check_flavor() def check_all_conditions_Phil_Frost(): for condition in all_conditions(): if condition: return condition def main(): num = 10000000 random.seed(20) print("Bernard:", timeit.timeit(''check_all_conditions_Bernard()'', ''from __main__ import check_all_conditions_Bernard'', number=num)) random.seed(20) print("Martijn Pieters:", timeit.timeit(''check_all_Martijn_Pieters()'', ''from __main__ import check_all_Martijn_Pieters'', number=num)) random.seed(20) print("timgeb:", timeit.timeit(''check_all_conditions_timgeb()'', ''from __main__ import check_all_conditions_timgeb'', number=num)) random.seed(20) print("Reza:", timeit.timeit(''check_all_conditions_Reza()'', ''from __main__ import check_all_conditions_Reza'', number=num)) random.seed(20) print("Phinet:", timeit.timeit(''check_all_conditions_Phinet()'', ''from __main__ import check_all_conditions_Phinet'', number=num)) random.seed(20) print("Phil Frost:", timeit.timeit(''check_all_conditions_Phil_Frost()'', ''from __main__ import check_all_conditions_Phil_Frost'', number=num)) if __name__ == ''__main__'': main()

Y aquí están los resultados:

Bernard: 7.398444877040768 Martijn Pieters: 8.506569201346597 timgeb: 7.244275416364456 Reza: 6.982133448743038 Phinet: 7.925932800076634 Phil Frost: 11.924794811353031


O use max :

def check_all_conditions(): return max(check_size(), check_color(), check_tone(), check_flavor()) or None


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 realizar verificaciones en el tipo de x para realizar cualquier trabajo adicional.

Entonces mezclaría la respuesta de @ PhilFrost con la idea de mantener un solo tipo:

def all_conditions(x): yield check_size(x) yield check_color(x) yield check_tone(x) yield check_flavor(x) def assessed_x(x,func=all_conditions): for condition in func(x): if condition: return x return None

Observe que x se pasa como argumento, pero también all_conditions se usa como un generador pasado de funciones de verificación donde todos obtienen una x para verificar y devuelven True o False . Al usar func con all_conditions como valor predeterminado, puede usar assessed_x(x) , o puede pasar un generador personalizado adicional a través de func .

De esa manera, obtienes x tan pronto como pasa un cheque, pero siempre será del mismo tipo.


Podrías usar un bucle:

conditions = (check_size, check_color, check_tone, check_flavor) for condition in conditions: result = condition() if result: return result

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 las versiones future_builtins en Python 2) para obtener el primer valor coincidente:

try: # Python 2 from future_builtins import map, filter except ImportError: # Python 3 pass conditions = (check_size, check_color, check_tone, check_flavor) return next(filter(None, map(lambda f: f(), conditions)), None)

pero si esto es más legible es discutible.

Otra opción es usar una expresión generadora:

conditions = (check_size, check_color, check_tone, check_flavor) checks = (condition() for condition in conditions) return next((check for check in checks if check), None)


Si desea la misma estructura de código, ¡podría usar declaraciones ternarias!

def check_all_conditions(): x = check_size() x = x if x else check_color() x = x if x else check_tone() x = x if x else check_flavor() return x if x else None

Creo que esto se ve bien y claro si lo miras.

Manifestación:


Una ligera variación en el primer ejemplo de Martijns anterior, que evita el if dentro del bucle:

Status = None for c in [check_size, check_color, check_tone, check_flavor]: Status = Status or c(); return Status


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í:

def check_all_conditions(): if (x := check_size()): return x if (x := check_color()): return x if (x := check_tone()): return x if (x := check_flavor()): return x return None


No lo cambies

Hay otras formas de hacerlo, como lo muestran las otras respuestas. Ninguno es tan claro como su código original.