relacionales - sentencias en python
¿Estilizando condiciones de multilínea en declaraciones ''if''? (29)
"todos" y "cualquiera" son agradables para las muchas condiciones del mismo tipo de caso. PERO siempre evalúan todas las condiciones. Como se muestra en este ejemplo:
def c1():
print " Executed c1"
return False
def c2():
print " Executed c2"
return False
print "simple and (aborts early!)"
if c1() and c2():
pass
print
print "all (executes all :( )"
if all((c1(),c2())):
pass
print
A veces rompo largas condiciones en if
está en varias líneas. La forma más obvia de hacer esto es:
if (cond1 == ''val1'' and cond2 == ''val2'' and
cond3 == ''val3'' and cond4 == ''val4''):
do_something
No es muy atractivo visualmente, porque la acción se combina con las condiciones. Sin embargo, es la forma natural de usar la sangría Python correcta de 4 espacios.
Por el momento estoy usando:
if ( cond1 == ''val1'' and cond2 == ''val2'' and
cond3 == ''val3'' and cond4 == ''val4''):
do_something
Pero esto no es muy bonito. :-)
¿Puedes recomendar una forma alternativa?
¿Qué pasa si solo insertamos una línea en blanco adicional entre la condición y el cuerpo y hacemos el resto de forma canónica?
if (cond1 == ''val1'' and cond2 == ''val2'' and
cond3 == ''val3'' and cond4 == ''val4''):
do_something
ps siempre uso tabulaciones, no espacios; No puedo afinar ...
(He modificado ligeramente los identificadores, ya que los nombres de ancho fijo no son representativos del código real, al menos no del código real con el que me encuentro, y desmienten la legibilidad de un ejemplo).
if (cond1 == "val1" and cond22 == "val2"
and cond333 == "val3" and cond4444 == "val4"):
do_something
Esto funciona bien para "y" y "o" (es importante que sean los primeros en la segunda línea), pero mucho menos para otras condiciones prolongadas. Afortunadamente, el primero parece ser el caso más común, mientras que el último suele ser fácilmente reescrito con una variable temporal. (Por lo general, no es difícil, pero puede ser difícil o mucho menos obvio / legible mantener el cortocircuito de "y" / "o" al reescribir).
Desde que encontré esta pregunta de tu blog sobre C ++ , incluiré que mi estilo de C ++ es idéntico:
if (cond1 == "val1" and cond22 == "val2"
and cond333 == "val3" and cond4444 == "val4") {
do_something
}
Agregando a lo que dijo @krawyoti ... Las condiciones duras huelen porque son difíciles de leer y de entender. El uso de una función o una variable hace que el código sea más claro. En Python, prefiero usar el espacio vertical, encerrar paréntesis y colocar los operadores lógicos al principio de cada línea para que las expresiones no parezcan "flotantes".
conditions_met = (
cond1 == ''val1''
and cond2 == ''val2''
and cond3 == ''val3''
and cond4 == ''val4''
)
if conditions_met:
do_something
Si es necesario evaluar las condiciones más de una vez, como en un bucle while, entonces lo mejor es usar una función local.
Aquí hay otro enfoque:
cond_list = [''cond1 == "val1"'',''cond2=="val2"'',''cond3=="val3"'',''cond4=="val4"'']
if all([eval(i) for i in cond_list]):
do something
Esto también hace que sea fácil agregar otra condición fácilmente sin cambiar la declaración if simplemente agregando otra condición a la lista:
cond_list.append(''cond5=="val5"'')
Creo que la solución de @zkanda sería buena con un pequeño giro. Si tuviera sus condiciones y valores en sus propias listas respectivas, podría usar una comprensión de lista para hacer la comparación, lo que haría las cosas un poco más generales para agregar pares de condición / valor.
conditions = [1, 2, 3, 4]
values = [1, 2, 3, 4]
if all([c==v for c, v in zip(conditions, values)]):
# do something
Si quisiera codificar una declaración como esta, la escribiría así para legibilidad:
if (condition1==value1) and (condition2==value2) and /
(condition3==value3) and (condition4==value4):
Y solo para lanzar otra solución con un operador iand
:
proceed = True
for c, v in zip(conditions, values):
proceed &= c==v
if proceed:
# do something
Empaque sus condiciones en una lista, luego haga algo. me gusta:
if False not in Conditions:
do_something
Encuentro que cuando tengo largas condiciones, a menudo tengo un cuerpo de código corto. En ese caso, simplemente sangré el cuerpo, así:
if (cond1 == ''val1'' and cond2 == ''val2'' and
cond3 == ''val3'' and cond4 == ''val4''):
do_something
Esta es mi opinión muy personal: las condiciones largas son (en mi opinión) un olor a código que sugiere refactorizar en una función / método de retorno booleano. Por ejemplo:
def is_action__required(...):
return (cond1 == ''val1'' and cond2 == ''val2''
and cond3 == ''val3'' and cond4 == ''val4'')
Ahora, si encuentro una manera de hacer que las condiciones de multilínea se vean bien, probablemente me satisfaría con tenerlas y me saltearía la refactorización.
Por otro lado, tenerlos perturbar mi sentido estético actúa como un incentivo para refactorizar.
Mi conclusión, por lo tanto, es que las condiciones de múltiples líneas deben verse feas y esto es un incentivo para evitarlas.
Esto es lo que hago, recuerde que "todos" y "cualquiera" aceptan un iterable, así que simplemente puse una condición larga en una lista y dejo que "todos" haga el trabajo.
condition = [cond1 == ''val1'', cond2 == ''val2'', cond3 == ''val3'', cond4 == ''val4'']
if all(condition):
do_something
Esto no mejora mucho pero ...
allCondsAreOK = (cond1 == ''val1'' and cond2 == ''val2'' and
cond3 == ''val3'' and cond4 == ''val4'')
if allCondsAreOK:
do_something
He recurrido a lo siguiente en el caso degenerado en el que se trata simplemente de AND u OR.
if all( [cond1 == ''val1'', cond2 == ''val2'', cond3 == ''val3'', cond4 == ''val4''] ):
if any( [cond1 == ''val1'', cond2 == ''val2'', cond3 == ''val3'', cond4 == ''val4''] ):
Afeita algunos caracteres y deja claro que no hay sutileza en la condición.
Llano y simple, también pasa cheques pep8:
if (
cond1 and
cond2
):
print("Hello World!")
En los últimos tiempos, he estado prefiriendo all
any
funciones, ya que rara vez mezclo las comparaciones de Y y O esto funciona bien, y tiene la ventaja adicional de Failing Early con la comprensión de los generadores:
if all([
cond1,
cond2,
]):
print("Hello World!")
Solo recuerda pasar en un solo iterable! Pasar en N-argumentos no es correcto.
Nota: any
es como muchos or
comparaciones, all
es como muchos and
comparaciones.
Esto combina muy bien con las comprensiones del generador, por ejemplo:
# Check if every string in a list contains a substring:
my_list = [
''a substring is like a string'',
''another substring''
]
if all(''substring'' in item for item in my_list):
print("Hello World!")
# or
if all(
''substring'' in item
for item in my_list
):
print("Hello World!")
Más sobre: comprensión de generador
Lo que suelo hacer es:
if (cond1 == ''val1'' and cond2 == ''val2'' and
cond3 == ''val3'' and cond4 == ''val4''
):
do_something
De esta manera, el corsé y el colon de cierre marcan visualmente el final de nuestra condición.
Me sorprende no ver mi solución preferida.
if (cond1 == ''val1'' and cond2 == ''val2''
and cond3 == ''val3'' and cond4 == ''val4''):
do_something
Ya que es una palabra clave, mi editor la resalta y se ve lo suficientemente diferente de do_something debajo de ella.
No necesitas usar 4 espacios en tu segunda línea condicional. Tal vez use:
if (cond1 == ''val1'' and cond2 == ''val2'' and
cond3 == ''val3'' and cond4 == ''val4''):
do_something
Además, no olvides que el espacio en blanco es más flexible de lo que piensas:
if (
cond1 == ''val1'' and cond2 == ''val2'' and
cond3 == ''val3'' and cond4 == ''val4''
):
do_something
if (cond1 == ''val1'' and cond2 == ''val2'' and
cond3 == ''val3'' and cond4 == ''val4''):
do_something
Sin embargo, ambos son bastante feos.
Tal vez pierda los corchetes (aunque la Guía de estilo desalienta esto)?
if cond1 == ''val1'' and cond2 == ''val2'' and /
cond3 == ''val3'' and cond4 == ''val4'':
do_something
Esto al menos te da alguna diferenciación.
O incluso:
if cond1 == ''val1'' and cond2 == ''val2'' and /
cond3 == ''val3'' and /
cond4 == ''val4'':
do_something
Creo que prefiero:
if cond1 == ''val1'' and /
cond2 == ''val2'' and /
cond3 == ''val3'' and /
cond4 == ''val4'':
do_something
Aquí está la guía de estilo , que (desde 2010) recomienda usar corchetes.
Parece que vale la pena citar el PEP 0008 (guía de estilo oficial de Python), ya que comenta sobre este tema con una extensión modesta:
Cuando la parte condicional de una declaración
if
es lo suficientemente larga como para requerir que se escriba en varias líneas, vale la pena señalar que la combinación de una palabra clave de dos caracteres (es decir,if
) más un espacio, más un paréntesis de apertura crea un espacio natural. Sangría de 4 espacios para las siguientes líneas del condicional multilínea. Esto puede generar un conflicto visual con el conjunto de código sangrado anidado dentro de la sentenciaif
, que también se sangraría naturalmente en 4 espacios. Este PEP no toma una posición explícita sobre cómo (o si) distinguir más visualmente dichas líneas condicionales de la suite anidada dentro de la declaraciónif
. Las opciones aceptables en esta situación incluyen, pero no se limitan a:
# No extra indentation. if (this_is_one_thing and that_is_another_thing): do_something() # Add a comment, which will provide some distinction in editors # supporting syntax highlighting. if (this_is_one_thing and that_is_another_thing): # Since both conditions are true, we can frobnicate. do_something() # Add some extra indentation on the conditional continuation line. if (this_is_one_thing and that_is_another_thing): do_something()
Tenga en cuenta el "no limitado a" en la cita anterior; además de los enfoques sugeridos en la guía de estilo, algunos de los sugeridos en otras respuestas a esta pregunta también son aceptables.
Personalmente, me gusta agregar un significado a las largas sentencias if. Tendría que buscar en el código para encontrar un ejemplo apropiado, pero aquí está el primer ejemplo que me viene a la mente: digamos que me encuentro con alguna lógica extraña donde quiero mostrar una página determinada dependiendo de muchas variables.
Inglés: "Si el usuario que ha iniciado sesión NO es un maestro administrador, sino que es solo un maestro regular, y no es un estudiante por sí mismo ..."
if not user.isAdmin() and user.isTeacher() and not user.isStudent():
doSomething()
Claro que esto puede verse bien, pero leer esas frases si es mucho trabajo. ¿Qué tal si asignamos la lógica a la etiqueta que tiene sentido. La "etiqueta" es en realidad el nombre de la variable:
displayTeacherPanel = not user.isAdmin() and user.isTeacher() and not user.isStudent()
if displayTeacherPanel:
showTeacherPanel()
Esto puede parecer una tontería, pero es posible que tenga otra condición en la que SOLO desee mostrar otro elemento si, y solo si, está mostrando el panel del profesor O si el usuario tiene acceso a ese otro panel específico de manera predeterminada:
if displayTeacherPanel or user.canSeeSpecialPanel():
showSpecialPanel()
Intente escribir la condición anterior sin usar variables para almacenar y etiquetar su lógica, y no solo termina con una declaración lógica muy complicada y difícil de leer, sino que también lo repitió. Si bien hay excepciones razonables, recuerde: no se repita (DRY).
Podrías dividirlo en dos líneas.
total = cond1 == ''val'' and cond2 == ''val2'' and cond3 == ''val3'' and cond4 == val4
if total:
do_something()
O incluso agregar en una condición a la vez. De esa manera, al menos separa el desorden del if
.
Prefiero este estilo cuando tengo una condición terriblemente grande:
if (
expr1
and (expr2 or expr3)
and hasattr(thingy1, ''__eq__'')
or status=="HappyTimes"
):
do_stuff()
else:
do_other_stuff()
Sé que este hilo es antiguo, pero tengo un código de Python 2.7 y PyCharm (4.5) aún se queja sobre este caso:
if foo is not None:
if (cond1 == ''val1'' and cond2 == ''val2'' and
cond3 == ''val3'' and cond4 == ''val4''):
# some comment about do_something
do_something
Incluso con la advertencia PEP8 "línea con sangría visual con la misma sangría que la siguiente línea lógica", ¿el código real está completamente bien? ¿No es "una sangría excesiva?"
... hay veces en las que desearía que Python hubiera mordido la bala y se hubiera ido con llaves. Me pregunto cuántos errores se han introducido accidentalmente a lo largo de los años debido a una sangría accidental ...
Sólo algunas otras ideas aleatorias por el bien de la integridad. Si trabajan para ti, úsalos. De lo contrario, probablemente sea mejor que intentes algo más.
También puedes hacer esto con un diccionario:
>>> x = {''cond1'' : ''val1'', ''cond2'' : ''val2''}
>>> y = {''cond1'' : ''val1'', ''cond2'' : ''val2''}
>>> x == y
True
Esta opción es más complicada, pero también puede resultarle útil:
class Klass(object):
def __init__(self, some_vars):
#initialize conditions here
def __nonzero__(self):
return (self.cond1 == ''val1'' and self.cond2 == ''val2'' and
self.cond3 == ''val3'' and self.cond4 == ''val4'')
foo = Klass()
if foo:
print "foo is true!"
else:
print "foo is false!"
No sé si eso funciona para ti, pero es otra opción a considerar. Aquí hay una manera más:
class Klass(object):
def __init__(self):
#initialize conditions here
def __eq__(self):
return (self.cond1 == ''val1'' and self.cond2 == ''val2'' and
self.cond3 == ''val3'' and self.cond4 == ''val4'')
x = Klass(some_values)
y = Klass(some_other_values)
if x == y:
print ''x == y''
else:
print ''x!=y''
Los dos últimos no los he probado, pero los conceptos deberían ser suficientes para ponerte en marcha si eso es lo que quieres.
(Y para que quede constancia, si esto es solo una vez, probablemente esté mejor usando el método que presentó al principio. Si está haciendo la comparación en muchos lugares, estos métodos pueden mejorar la legibilidad lo suficiente para que no te sientes tan mal por el hecho de que son una especie de hacky.)
Si nuestra condición if & an else tiene que ejecutar varias sentencias dentro de ella, podemos escribirla a continuación. Cada vez que tenemos otro ejemplo con una declaración dentro de ella.
Gracias funciona para mí.
#!/usr/bin/python
import sys
numberOfArgument =len(sys.argv)
weblogic_username =''''
weblogic_password = ''''
weblogic_admin_server_host =''''
weblogic_admin_server_port =''''
if numberOfArgument == 5:
weblogic_username = sys.argv[1]
weblogic_password = sys.argv[2]
weblogic_admin_server_host =sys.argv[3]
weblogic_admin_server_port=sys.argv[4]
elif numberOfArgument <5:
print " weblogic UserName, weblogic Password and weblogic host details are Mandatory like, defalutUser, passwordForDefaultUser, t3s://server.domainname:7001 ."
weblogic_username = raw_input("Enter Weblogic user Name")
weblogic_password = raw_input(''Enter Weblogic user Password'')
weblogic_admin_server_host = raw_input(''Enter Weblogic admin host '')
weblogic_admin_server_port = raw_input(''Enter Weblogic admin port'')
#enfelif
#endIf
Suelo usar:
if ((cond1 == ''val1'' and cond2 == ''val2'' and
cond3 == ''val3'' and cond4 == ''val4'')):
do_something()
Sugiero mover la palabra clave and
a la segunda línea y sangrar todas las líneas que contienen condiciones con dos espacios en lugar de cuatro:
if (cond1 == ''val1'' and cond2 == ''val2''
and cond3 == ''val3'' and cond4 == ''val4''):
do_something
Así es exactamente como soluciono este problema en mi código. Tener una palabra clave como la primera palabra en la línea hace que la condición sea mucho más legible, y reducir el número de espacios distingue más la condición de la acción.
También he estado luchando para encontrar una manera decente de hacer esto, así que se me ocurrió una idea (no una bala de plata, ya que esto es principalmente una cuestión de gustos).
if bool(condition1 and
condition2 and
...
conditionN):
foo()
bar()
Encuentro algunos méritos en esta solución en comparación con otros que he visto, a saber, obtienes exactamente 4 espacios adicionales de sangría (bool), lo que permite que todas las condiciones se alineen verticalmente, y el cuerpo de la declaración if puede sangrarse en una manera clara (ish) Esto también mantiene los beneficios de la evaluación en corto circuito de operadores booleanos, pero, por supuesto, agrega la sobrecarga de una llamada de función que básicamente no hace nada. Podría argumentar (válidamente) que cualquier función que devuelva su argumento podría usarse aquí en lugar de bool, pero como dije, es solo una idea y, en última instancia, es una cuestión de gustos.
Lo suficientemente divertido, mientras escribía esto y pensaba en el "problema", se me ocurrió otra idea, que elimina la sobrecarga de una llamada de función. ¿Por qué no indicar que estamos por ingresar a una condición compleja utilizando pares de paréntesis adicionales? Decir, 2 más, para dar una bonita sangría de 2 espacios de las sub-condiciones relativas al cuerpo de la sentencia if. Ejemplo:
if (((foo and
bar and
frob and
ninja_bear))):
do_stuff()
Me gusta esto porque cuando lo miras, inmediatamente suena una campana en tu cabeza que dice "hey, ¡aquí está pasando algo complejo!" . Sí, sé que los paréntesis no ayudan a la legibilidad, pero estas condiciones deben aparecer lo suficientemente raras y, cuando aparezcan, tendrá que detenerse y leerlas con cuidado (porque son complejas ).
De todos modos, solo hay dos propuestas más que no he visto aquí. Espero que esto ayude a alguien :)
Todos los encuestados que también proporcionan condicionales múltiples para la sentencia if son tan feos como el problema presentado. No resuelves este problema haciendo lo mismo ..
Incluso la respuesta PEP 0008 es repulsiva.
Aquí hay un enfoque mucho más legible
condition = random.randint(0, 100) # to demonstrate
anti_conditions = [42, 67, 12]
if condition not in anti_conditions:
pass
¿Quieres que me coma mis palabras? Convénceme que necesitas condicionales múltiples y literalmente imprimiré esto y lo comeré para tu diversión.
¡Alguien tiene que defender el uso del espacio en blanco vertical aquí! :)
if ( cond1 == val1
and cond2 == val2
and cond3 == val3
):
do_stuff()
Esto hace que cada condición sea claramente visible. También permite una expresión más limpia de condiciones más complejas:
if ( cond1 == val1
or
( cond2_1 == val2_1
and cond2_2 >= val2_2
and cond2_3 != bad2_3
)
):
do_more_stuff()
Sí, estamos cambiando un poco de bienes raíces verticales para mayor claridad. Bien vale la pena IMO.
if cond1 == ''val1'' and /
cond2 == ''val2'' and /
cond3 == ''val3'' and /
cond4 == ''val4'':
do_something
o si esto es más claro:
if cond1 == ''val1''/
and cond2 == ''val2''/
and cond3 == ''val3''/
and cond4 == ''val4'':
do_something
No hay razón para que la sangría sea un múltiplo de 4 en este caso, por ejemplo, consulte "Alineado con el delimitador de apertura":
http://google-styleguide.googlecode.com/svn/trunk/pyguide.html?showone=Indentation#Indentation