web-dev-qa-db-fra.com

Mise en forme de conditions multilignes dans les déclarations 'if'?

Parfois, je casse de longues conditions dans ifs sur plusieurs lignes. La manière la plus évidente de le faire est:

  if (cond1 == 'val1' and cond2 == 'val2' and
      cond3 == 'val3' and cond4 == 'val4'):
      do_something

N’est pas très attrayant visuellement, parce que l’action se confond avec les conditions. Cependant, c’est la méthode naturelle qui consiste à utiliser une bonne indentation Python de 4 espaces.

Pour le moment j'utilise:

  if (    cond1 == 'val1' and cond2 == 'val2' and
          cond3 == 'val3' and cond4 == 'val4'):
      do_something

Mais ce n'est pas très joli. :-)

Pouvez-vous recommander un autre moyen?

530
Eli Bendersky

Vous n'avez pas besoin d'utiliser 4 espaces sur votre deuxième ligne conditionnelle. Peut-être utiliser:

if (cond1 == 'val1' and cond2 == 'val2' and 
       cond3 == 'val3' and cond4 == 'val4'):
    do_something

De plus, n'oubliez pas que les espaces sont plus flexibles que vous ne le pensez:

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

Les deux sont assez moche cependant.

Peut-être perdre les crochets (le Style Guide décourage cependant cela)?

if cond1 == 'val1' and cond2 == 'val2' and \
   cond3 == 'val3' and cond4 == 'val4':
    do_something

Cela vous donne au moins une certaine différenciation.

Ou même:

if cond1 == 'val1' and cond2 == 'val2' and \
                       cond3 == 'val3' and \
                       cond4 == 'val4':
    do_something

Je pense que je préfère:

if cond1 == 'val1' and \
   cond2 == 'val2' and \
   cond3 == 'val3' and \
   cond4 == 'val4':
    do_something

Voici le Style Guide , qui (depuis 2010) recommande l’utilisation de crochets.

576
Harley Holcombe

J'ai eu recours à ce qui suit dans le cas dégénéré où il s'agit simplement d'ET ou de OU.

if all( [cond1 == 'val1', cond2 == 'val2', cond3 == 'val3', cond4 == 'val4'] ):

if any( [cond1 == 'val1', cond2 == 'val2', cond3 == 'val3', cond4 == 'val4'] ):

Il rase quelques caractères et montre clairement qu'il n'y a pas de subtilité dans la condition.

105
S.Lott

Quelqu'un doit défendre l'utilisation des espaces verticaux ici! :)

if (     cond1 == val1
     and cond2 == val2
     and cond3 == val3
   ):
    do_stuff()

Cela rend chaque condition clairement visible. Il permet également une expression plus propre de conditions plus complexes:

if (    cond1 == val1
     or 
        (     cond2_1 == val2_1
          and cond2_2 >= val2_2
          and cond2_3 != bad2_3
        )
   ):
    do_more_stuff()

Oui, nous échangeons un peu d’immobilier vertical pour plus de clarté. Ça vaut la peine IMO.

48
Kevin Little

Voici mon point de vue personnel: les longues conditions sont (à mon avis) une odeur de code qui suggère de refactoriser dans une fonction/méthode de retour booléen. Par exemple:

def is_action__required(...):
    return (cond1 == 'val1' and cond2 == 'val2'
            and cond3 == 'val3' and cond4 == 'val4')

Maintenant, si je trouvais un moyen de rendre bonnes les conditions multilignes, je me contenterais probablement de les avoir et d'éviter le refactoring.

D'autre part, leur perturbation mon sens esthétique agit comme une incitation à la refactorisation.

Ma conclusion est donc que les conditions de ligne multiples devraient sembler laides et que cela les incite à les éviter.

23
krawyoti

Cela ne s'améliore pas tellement mais ...

allCondsAreOK = (cond1 == 'val1' and cond2 == 'val2' and
                 cond3 == 'val3' and cond4 == 'val4')

if allCondsAreOK:
   do_something
21

Je préfère ce style quand j'ai un si grand état si:

if (
    expr1
    and (expr2 or expr3)
    and hasattr(thingy1, '__eq__')
    or status=="HappyTimes"
):
    do_stuff()
else:
    do_other_stuff()
20
Deestan

Je suggère de déplacer le mot clé and vers la deuxième ligne et d'indenter toutes les lignes contenant des conditions avec deux espaces au lieu de quatre:

if (cond1 == 'val1' and cond2 == 'val2'
  and cond3 == 'val3' and cond4 == 'val4'):
    do_something

C’est exactement comme cela que je résous ce problème dans mon code. Avoir un mot-clé comme premier mot de la ligne rend la condition beaucoup plus lisible, et réduire le nombre d'espaces différencie davantage la condition de l'action.

19
DzinX

Il semble intéressant de citer PEP 0008 (guide de style officiel de Python), car il commente cette question de manière modeste:

Lorsque la partie conditionnelle d'une instruction if est suffisamment longue pour exiger son écriture sur plusieurs lignes, il est intéressant de noter que la combinaison d'un mot clé à deux caractères (ie if), d'un espace unique et d'une parenthèse ouvrante crée Retrait de 4 espaces pour les lignes suivantes du multiligne conditionnel. Cela peut produire un conflit visuel avec la suite de code indentée imbriquée dans l'instruction if, qui serait aussi naturellement indentée de 4 espaces. Ce PEP ne prend pas de position explicite sur la manière (ou non) de distinguer visuellement davantage ces lignes conditionnelles de la suite imbriquée à l'intérieur de l'instruction if. Les options acceptables dans cette situation incluent, mais ne sont pas limitées à:

# 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()

Notez le "non limité à" dans la citation ci-dessus; En plus des approches suggérées dans le guide de style, certaines de celles suggérées dans d'autres réponses à cette question sont également acceptables.

10
Mark Amery

Voici ce que je fais, rappelez-vous que "tous" et "tous" acceptent les valeurs éditables, alors je mets simplement une longue condition dans une liste et laisse "tous" faire le travail.

condition = [cond1 == 'val1', cond2 == 'val2', cond3 == 'val3', cond4 == 'val4']

if all(condition):
   do_something
5
zkanda

Je suis surpris de ne pas voir ma solution préférée,

if (cond1 == 'val1' and cond2 == 'val2'
    and cond3 == 'val3' and cond4 == 'val4'):
    do_something

Étant donné que and est un mot clé, il est mis en évidence par mon éditeur et est suffisamment différent du do_something situé en dessous.

4
Marius Gedminas

Personnellement, j'aime ajouter du sens à de longues déclarations if. Il faudrait que je cherche dans le code pour trouver un exemple approprié, mais voici le premier exemple qui me vient à l’esprit: disons que j’ai un problème avec une logique étrange dans laquelle je veux afficher une certaine page en fonction de nombreuses variables.

Anglais: "Si l'utilisateur connecté n'est PAS un enseignant administrateur, mais bien un enseignant régulier et n'est pas un étudiant lui-même ..."

if not user.isAdmin() and user.isTeacher() and not user.isStudent():
    doSomething()

Bien sûr, cela peut sembler correct, mais la lecture de ces déclarations représente beaucoup de travail. Pourquoi ne pas assigner la logique à une étiquette qui a du sens. Le "label" est en fait le nom de la variable:

displayTeacherPanel = not user.isAdmin() and user.isTeacher() and not user.isStudent()
if displayTeacherPanel:
    showTeacherPanel()

Cela peut sembler idiot, mais vous pouvez également avoir une autre condition dans laquelle vous voulez UNIQUEMENT afficher un autre élément si, et seulement si, vous affichez le panneau de l'enseignant OR si l'utilisateur a accès à cet autre panneau spécifique par défaut :

if displayTeacherPanel or user.canSeeSpecialPanel():
    showSpecialPanel()

Essayez d'écrire la condition ci-dessus sans utiliser de variables pour stocker et étiqueter votre logique. Non seulement vous obtenez une instruction logique très compliquée et difficile à lire, mais vous vous répétez tout simplement. S’il existe des exceptions raisonnables, rappelez-vous: ne vous répétez pas (DRY).

3
rgenito

En plus de ce que @krawyoti a dit ... Les longues conditions sentent mauvais, car elles sont difficiles à lire et à comprendre. L'utilisation d'une fonction ou d'une variable rend le code plus clair. En Python, je préfère utiliser l’espace vertical, mettre entre parenthèses et placer les opérateurs logiques au début de chaque ligne afin que les expressions ne ressemblent pas à "flottantes".

conditions_met = (
    cond1 == 'val1' 
    and cond2 == 'val2' 
    and cond3 == 'val3' 
    and cond4 == 'val4'
    )
if conditions_met:
    do_something

Si les conditions doivent être évaluées plusieurs fois, comme dans une boucle while, il est préférable d'utiliser une fonction locale.

3
Apalala

"all" et "any" conviennent aux nombreuses conditions du même type. MAIS ils évaluent toujours toutes les conditions. Comme indiqué dans cet exemple:

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
3

(J'ai légèrement modifié les identifiants car les noms à largeur fixe ne sont pas représentatifs du code réel - du moins pas du code réel que je rencontre - et vont démentir la lisibilité d'un exemple.)

if (cond1 == "val1" and cond22 == "val2"
and cond333 == "val3" and cond4444 == "val4"):
    do_something

Cela fonctionne bien pour "et" et "ou" (il est important qu'ils soient en première position sur la deuxième ligne), mais beaucoup moins pour d'autres conditions longues. Heureusement, les premiers semblent être le cas le plus courant alors que les derniers sont souvent facilement réécrites avec une variable temporaire. (Ce n'est généralement pas difficile, mais il peut être difficile ou beaucoup moins évident/lisible de conserver le court-circuit de "et"/"ou" lors de la réécriture.)

Depuis que j'ai trouvé cette question dans votre billet de blog sur C++ , j'inclue que mon style C++ est identique:

if (cond1 == "val1" and cond22 == "val2"
and cond333 == "val3" and cond4444 == "val4") {
    do_something
}
3
Fred Nurk

Clair et simple, passe également les contrôles pep8:

if (
    cond1 and
    cond2
):
    print("Hello World!")

Ces derniers temps, je préférais les fonctions all et any, étant donné que je mélange rarement les comparaisons And and Or et que cela fonctionne bien.

if all([
    cond1,
    cond2,
]):
    print("Hello World!")

N'oubliez pas de passer dans un seul itérable! Passer dans N-arguments n'est pas correct.

Remarque: any est semblable à beaucoup de comparaisons or, all est semblable à beaucoup de comparaisons and.


Ceci se combine bien avec les compréhensions de générateur, par exemple:

# 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!")

Plus sur: compréhension du générateur

3
ThorSummoner

Et si nous insérons seulement une ligne vierge supplémentaire entre la condition et le corps et faisons le reste de manière canonique?

if (cond1 == 'val1' and cond2 == 'val2' and
    cond3 == 'val3' and cond4 == 'val4'):

    do_something

p.s. J'utilise toujours des tabulations, pas des espaces; Je ne peux pas peaufiner ...

2

Ce que je fais habituellement c'est:

if (cond1 == 'val1' and cond2 == 'val2' and
    cond3 == 'val3' and cond4 == 'val4'
   ):
    do_something

de cette façon, le corset et le colon marquent visuellement la fin de notre condition.

1
tomekwi

J'ai eu du mal à trouver un moyen décent de faire cela aussi, alors je viens de proposer une idée (pas une solution miracle, car il s'agit principalement d'une question de goût).

if bool(condition1 and
        condition2 and
        ...
        conditionN):
    foo()
    bar()

Je trouve quelques avantages dans cette solution par rapport aux autres que j'ai vus, à savoir, vous obtenez exactement 4 espaces d'indentation (bool) supplémentaires, permettant à toutes les conditions de s'aligner verticalement, et le corps de la déclaration if peut être indenté dans une voie claire (ish). Cela conserve également les avantages de l’évaluation de court-circuit des opérateurs booléens, mais ajoute évidemment la surcharge d’un appel de fonction qui ne fait fondamentalement rien. Vous pourriez argumenter (valablement) que toute fonction renvoyant son argument pourrait être utilisée ici au lieu de bool, mais comme je l'ai dit, c'est juste une idée et c'est finalement une question de goût.

Assez drôle, alors que j’écrivais ceci et pensais au "problème", j’ai eu l’idée de encore une autre idée, qui supprime la surcharge d’un appel de fonction. Pourquoi ne pas indiquer que nous sommes sur le point d'entrer dans une condition complexe en utilisant des paires de parenthèses supplémentaires? Disons 2 de plus, pour donner un retrait de Nice 2 espace des sous-conditions relatives au corps de l'instruction if. Exemple:

if (((foo and
      bar and
      frob and
      ninja_bear))):
    do_stuff()

J'aime bien ça parce que quand vous le regardez, une cloche sonne immédiatement dans votre tête en disant (hé, il y a une chose complexe qui se passe ici! ". Oui, je sais que les parenthèses ne facilitent pas la lisibilité, mais ces conditions devraient apparaître assez rarement et, lorsqu'elles apparaissent, vous devrez vous arrêter et les lire attentivement (car elles sont complexes) .

Quoi qu'il en soit, juste deux autres propositions que je n'ai pas vu ici. J'espère que ça aide quelqu'un :)

1
El Ninja Trepador

Juste quelques autres idées aléatoires pour la perfection. S'ils travaillent pour vous, utilisez-les. Sinon, vous feriez probablement mieux d'essayer autre chose.

Vous pouvez aussi le faire avec un dictionnaire:

>>> x = {'cond1' : 'val1', 'cond2' : 'val2'}
>>> y = {'cond1' : 'val1', 'cond2' : 'val2'}
>>> x == y
True

Cette option est plus compliquée, mais vous pouvez également la trouver utile:

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!"

Ne sais pas si cela fonctionne pour vous, mais c'est une autre option à considérer. Voici un autre moyen:

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'

Les deux derniers n'ont pas encore été testés, mais les concepts devraient suffire à vous lancer, si c'est ce que vous voulez faire.

(Et pour mémoire, s’il s’agit d’une chose ponctuelle, vous feriez probablement mieux d’utiliser la méthode que vous avez présentée au début. Si vous faites la comparaison dans de nombreux endroits, ces méthodes peuvent améliorer la lisibilité suffisamment vous ne vous sentez pas si mal à propos du fait qu'ils sont un peu hacky.)

1
Jason Baker

Vous pouvez le scinder en deux lignes

total = cond1 == 'val' and cond2 == 'val2' and cond3 == 'val3' and cond4 == val4
if total:
    do_something()

Ou même ajouter une condition à la fois. De cette façon, au moins cela sépare le fouillis de la if.

1
SarcasticSully

Je sais que ce fil est ancien, mais j'ai du code Python 2.7 et PyCharm (4.5) se plaint toujours de cette affaire:

if foo is not None:
    if (cond1 == 'val1' and cond2 == 'val2' and
        cond3 == 'val3' and cond4 == 'val4'):
            # some comment about do_something
            do_something

Même avec l'avertissement PEP8 "ligne indentée visuellement avec le même retrait que la ligne logique suivante", le code actuel est-il complètement correct? Ce n'est pas "trop ​​indenté?"

... il y a des moments où j'aurais aimé que Python morde la balle et partit avec des accolades. Je me demande combien de bugs ont été introduits accidentellement au cours des années en raison d'une erreur d'indentation accidentelle ...

1
SMGreenfield

Je pense que la solution de @ zkanda serait bonne avec une légère modification. Si vos conditions et vos valeurs figurent dans leurs propres listes respectives, vous pouvez utiliser une compréhension de liste pour faire la comparaison, ce qui rendrait les choses un peu plus générales pour l'ajout de paires condition/valeur.

conditions = [1, 2, 3, 4]
values = [1, 2, 3, 4]
if all([c==v for c, v in Zip(conditions, values)]):
    # do something

Si je voulais coder en dur une déclaration comme celle-ci, je l'écrirais ainsi pour des raisons de lisibilité:

if (condition1==value1) and (condition2==value2) and \
   (condition3==value3) and (condition4==value4):

Et juste pour lancer une autre solution avec un opérateur iand :

proceed = True
for c, v in Zip(conditions, values):
    proceed &= c==v

if proceed:
    # do something
1
ryanjdillon

si notre condition if & an else doit exécuter plusieurs instructions à l'intérieur, nous pouvons écrire comme ci-dessous .

Merci cela fonctionne pour moi.

#!/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
0
Laxman G

Je trouve que quand j'ai de longues conditions, j'ai souvent un corps de code court. Dans ce cas, je double-indenter le corps, ainsi:

if (cond1 == 'val1' and cond2 == 'val2' and
    cond3 == 'val3' and cond4 == 'val4'):
        do_something
0
xorsyst

J'utilise habituellement: 

if ((cond1 == 'val1' and cond2 == 'val2' and
     cond3 == 'val3' and cond4 == 'val4')):
    do_something()
0
Artur Gaspar

Emballez vos conditions dans une liste, puis faites-le. comme:

if False not in Conditions:
    do_something
0
psihodelia

Tous les répondants qui fournissent également plusieurs conditions pour la déclaration if sont aussi moche que le problème présenté. Vous ne résolvez pas ce problème en faisant la même chose .. 

Même la réponse PEP 0008 est répugnante.

Voici une approche beaucoup plus lisible

condition = random.randint(0, 100) # to demonstrate
anti_conditions = [42, 67, 12]
if condition not in anti_conditions:
    pass

Tu veux que je mange mes mots? Convainquez-moi que vous avez besoin de plusieurs conditions et je vais littéralement l'imprimer et le manger pour votre divertissement.

0
Stoff

Voici une autre approche:

cond_list = ['cond1 == "val1"','cond2=="val2"','cond3=="val3"','cond4=="val4"']
if all([eval(i) for i in cond_list]):
 do something

Cela facilite également l'ajout d'une autre condition sans modifier l'instruction if en ajoutant simplement une autre condition à la liste:

cond_list.append('cond5=="val5"')
0
user1487551

Je suppose que quelque chose comme ceci est l'option la plus lisible:

for pet in Zoo:
    cute = every_pet()
    furry = hair is 'over9000'
    small = size < min_size
    if cute and furry and small:
        return 'I need to feed it!'
0
Alex
  if cond1 == 'val1' and \
     cond2 == 'val2' and \
     cond3 == 'val3' and \
     cond4 == 'val4':
      do_something

ou si cela est plus clair:

  if cond1 == 'val1'\
     and cond2 == 'val2'\
     and cond3 == 'val3'\
     and cond4 == 'val4':
      do_something

Il n'y a pas de raison que le retrait soit un multiple de 4 dans ce cas, par exemple. voir "Aligné avec le séparateur d'ouverture":

http://google-styleguide.googlecode.com/svn/trunk/pyguide.html?showone=Indentation#Indentation

0
Dima Tisnek