web-dev-qa-db-de.com

Mehrzeilige Bedingungen in 'if'-Anweisungen gestalten

Manchmal zerlege ich lange Bedingungen in ifs auf mehrere Zeilen. Der offensichtlichste Weg, dies zu tun, ist:

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

Ist optisch nicht sehr ansprechend, da sich die Aktion mit den Bedingungen vermischt. Es ist jedoch der natürliche Weg unter Verwendung der korrekten Python-Einrückung von 4 Leerzeichen.

Im Moment verwende ich:

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

Das ist aber nicht sehr hübsch. :-)

Kannst du einen alternativen Weg empfehlen?

530
Eli Bendersky

Sie müssen nicht 4 Leerzeichen in Ihrer zweiten Bedingungszeile verwenden. Vielleicht verwenden:

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

Vergessen Sie nicht, dass der Leerraum flexibler ist, als Sie vielleicht denken:

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

Beide sind jedoch ziemlich hässlich.

Vielleicht verlieren Sie die Klammern (der Style Guide entmutigt dies jedoch).

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

Dies gibt Ihnen zumindest eine Differenzierung.

Oder auch:

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

Ich denke ich bevorzuge:

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

Hier ist der Style Guide , der (seit 2010) die Verwendung von Klammern empfiehlt.

576
Harley Holcombe

Ich habe im degenerierten Fall, wo es einfach AND oder OR ist, auf folgendes zurückgegriffen.

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

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

Es rasiert ein paar Zeichen und macht deutlich, dass der Zustand nicht subtil ist.

105
S.Lott

Jemand muss sich hier für die Verwendung von vertikalem Leerzeichen einsetzen! :)

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

Dadurch wird jeder Zustand deutlich sichtbar. Es ermöglicht auch einen saubereren Ausdruck komplexerer Bedingungen:

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

Ja, wir tauschen ein bisschen vertikales Grundstück aus, um Klarheit zu schaffen. Es lohnt sich IMO.

48
Kevin Little

Hier ist mein ganz persönlicher Ansatz: Lange Bedingungen sind (aus meiner Sicht) ein Codegeruch, der die Umgestaltung in eine boolesche Funktion/Methode vorschlägt. Zum Beispiel:

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

Wenn ich nun einen Weg gefunden hätte, die Bedingungen für mehrere Zeilen gut aussehen zu lassen, würde ich mich wahrscheinlich damit zufrieden geben, sie zu haben und das Refactoring zu überspringen.

Auf der anderen Seite, wenn sie meinen ästhetischen Sinn stören, ist dies ein Anreiz für das Refactoring.

Meine Schlussfolgerung ist daher, dass die Bedingungen für mehrere Leitungen hässlich aussehen sollten, und dies ist ein Anreiz, sie zu vermeiden.

23
krawyoti

Das verbessert sich nicht so sehr, aber ...

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

if allCondsAreOK:
   do_something
21

Ich bevorzuge diesen Stil, wenn ich eine schrecklich große If-Bedingung habe:

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

Ich schlage vor, das Schlüsselwort and in die zweite Zeile zu verschieben und alle Zeilen, die Bedingungen enthalten, mit zwei statt vier Leerzeichen einzurücken:

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

Genau so löse ich dieses Problem in meinem Code. Ein Schlüsselwort als erstes Wort in der Zeile macht die Bedingung viel lesbarer, und die Verringerung der Anzahl der Leerzeichen unterscheidet die Bedingung zusätzlich von der Aktion.

19
DzinX

Es scheint wert zu sein, PEP 0008 (Pythons offizieller Styleguide) zu zitieren, da er dieses Thema in bescheidener Länge kommentiert:

Wenn der bedingte Teil einer if -Anweisung lang genug ist, um mehrere Zeilen enthalten zu können, ist es erwähnenswert, dass die Kombination aus einem zweistelligen Schlüsselwort (dh if) plus einem einzelnen Leerzeichen und einer öffnenden Klammer eine natürliche Zahl erzeugt 4-Raum-Einzug für die nachfolgenden Zeilen der mehrzeiligen Bedingung. Dies kann zu einem visuellen Konflikt mit der eingerückten Code-Suite führen, die in der if -Statement verschachtelt ist, die natürlich auch um 4 Leerzeichen eingerückt wäre. Dieses PEP nimmt keine explizite Position ein, wie (oder ob) solche Bedingungszeilen visuell von der verschachtelten Suite innerhalb der if -Anweisung unterschieden werden sollen. Zu den akzeptablen Optionen in dieser Situation gehören unter anderem:

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

Beachten Sie das "nicht beschränkt auf" im obigen Zitat. Abgesehen von den im Style Guide vorgeschlagenen Ansätzen sind einige der in anderen Antworten auf diese Frage vorgeschlagenen auch akzeptabel.

10
Mark Amery

Hier ist, was ich tue, denke daran, dass "all" und "any" ein iterierbares Element akzeptieren. Ich habe also eine lange Bedingung in eine Liste aufgenommen und "all" die Arbeit erledigen lassen.

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

if all(condition):
   do_something
5
zkanda

Ich bin überrascht, meine bevorzugte Lösung nicht zu sehen.

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

Da and ein Schlüsselwort ist, wird es von meinem Editor hervorgehoben und unterscheidet sich ausreichend von dem darunter liegenden do_something-Objekt.

4
Marius Gedminas

Persönlich möchte ich langen if-Anweisungen eine Bedeutung hinzufügen. Ich müsste nach Code suchen, um ein passendes Beispiel zu finden, aber hier ist das erste Beispiel, das mir in den Sinn kommt: Angenommen, ich stolpere mit einer komischen Logik, bei der ich eine bestimmte Seite anzeigen möchte, die von vielen Variablen abhängig ist.

Englisch: "Wenn der angemeldete Benutzer KEIN Administratorlehrer ist, sondern nur ein regulärer Lehrer und selbst kein Schüler ist ..."

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

Sicher sieht das gut aus, aber das Lesen dieser Aussagen ist eine Menge Arbeit. Wie wäre es, wenn wir die Logik einer sinnvollen Kennzeichnung zuweisen. Das "Label" ist eigentlich der Variablenname:

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

Dies mag dumm erscheinen, aber es gibt möglicherweise eine andere Bedingung, bei der Sie NUR ein anderes Element anzeigen möchten, und nur dann, wenn Sie das Lehrerfenster OR anzeigen, wenn der Benutzer standardmäßig Zugriff auf dieses andere bestimmte Fenster hat :

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

Versuchen Sie, die obige Bedingung zu schreiben, ohne Variablen zu verwenden, um Ihre Logik zu speichern und zu kennzeichnen, und Sie erhalten nicht nur eine sehr unordentliche, schwer lesbare logische Anweisung, sondern Sie haben sich auch selbst wiederholt. Denken Sie daran, dass es vernünftige Ausnahmen gibt: Nicht wiederholen (DRY).

3
rgenito

Hinzufügen zu dem, was @krawyoti gesagt hat ... Lange Zustände riechen, weil sie schwer zu lesen und schwer zu verstehen sind. Die Verwendung einer Funktion oder einer Variablen macht den Code klarer. In Python ziehe ich es vor, vertikalen Raum zu verwenden, Klammern zu verwenden und die logischen Operatoren an den Anfang jeder Zeile zu setzen, damit die Ausdrücke nicht wie "schwebend" aussehen.

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

Wenn die Bedingungen wie in einer while-Schleife mehr als einmal ausgewertet werden müssen, ist die Verwendung einer lokalen Funktion am besten.

3
Apalala

"all" und "any" sind Nizza für die vielen Bedingungen des gleichen Typs. ABER sie wertet immer alle Bedingungen aus. Wie in diesem Beispiel gezeigt:

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

(Ich habe die Bezeichner leicht modifiziert, da Namen mit fester Breite nicht für echten Code repräsentativ sind - zumindest nicht für echten Code, dem ich begegne - und werden die Lesbarkeit eines Beispiels für richtig halten.)

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

Dies funktioniert gut für "und" und "oder" (es ist wichtig, dass sie die erste in der zweiten Zeile sind), aber noch viel weniger für andere lange Bedingungen. Glücklicherweise scheint Ersteres der häufigere Fall zu sein, während Letzteres oft leicht mit einer temporären Variable überschrieben werden kann. (Es ist normalerweise nicht schwer, aber es kann schwierig oder weniger offensichtlich sein, den Kurzschluss von "und"/"oder" beim Umschreiben zu erhalten.)

Da ich diese Frage aus Ihrem Blogbeitrag zu C++ gefunden habe, möchte ich hinzufügen, dass mein C++ - Stil identisch ist:

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

Einfach und einfach, besteht auch pep8-Prüfungen:

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

In letzter Zeit habe ich die Funktionen all und any vorgezogen, da ich selten Und-Oder-Vergleiche mische, was gut funktioniert und den zusätzlichen Vorteil hat, dass das Frühere mit dem Verständnis von Generatoren versagt:

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

Denken Sie daran, einen einzigen iterierbaren Code zu übergeben! Die Übergabe von N-Argumenten ist nicht korrekt.

Hinweis: any ist wie viele or Vergleiche, all ist wie viele and Vergleiche.


Dies lässt sich gut mit Generator-Verständnis kombinieren, zum Beispiel:

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

Mehr zu: Generatorverständnis

3
ThorSummoner

Was passiert, wenn wir nur eine zusätzliche Leerzeile zwischen Zustand und Körper einfügen und den Rest auf kanonische Weise erledigen?

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

    do_something

p.s. Ich verwende immer Tabulatoren, keine Leerzeichen. Ich kann nicht fein einstellen ...

2

Was ich normalerweise mache ist:

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

auf diese Weise markieren die schließende Klammer und der Doppelpunkt visuell das Ende unseres Zustands.

1
tomekwi

Ich hatte Mühe, auch einen vernünftigen Weg zu finden, um das zu tun, also kam mir gerade eine Idee (keine Silberkugel, da dies hauptsächlich eine Frage des Geschmacks ist).

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

Ich finde ein paar Vorzüge in dieser Lösung im Vergleich zu anderen, die ich gesehen habe, nämlich, Sie bekommen genau 4 zusätzliche Einrückungsbereiche (bool), wodurch alle Bedingungen vertikal ausgerichtet werden können, und der Rumpf der if-Anweisung kann eingerückt werden ein klarer (ish) Weg. Dies behält auch die Vorteile der Kurzschlussbewertung von Booleschen Operatoren bei, fügt jedoch den Aufwand eines Funktionsaufrufs hinzu, der im Grunde nichts bewirkt. Man könnte (gültig) argumentieren, dass jede Funktion, die ihr Argument zurückgibt, hier anstelle von bool verwendet werden könnte, aber wie gesagt, es ist nur eine Idee und letztendlich eine Geschmackssache.

Witzigerweise habe ich, als ich dies schrieb und über das "Problem" nachdachte, die Idee noch eine weitere, die den Aufwand eines Funktionsaufrufs beseitigt. Warum geben Sie nicht an, dass wir eine komplexe Bedingung eingehen, indem Sie zusätzliche Klammerpaare verwenden? Sagen Sie noch zwei weitere, um den Einzug von Nice 2 um die Teilbedingungen relativ zum Rumpf der if-Anweisung festzulegen. Beispiel:

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

Ich mag das so, denn wenn Sie es betrachten, klingelt sofort eine Glocke in Ihrem Kopf und sagt "Hey, hier ist eine komplexe Sache!". Ja, ich weiß, dass Klammern nicht zur besseren Lesbarkeit beitragen, aber diese Bedingungen sollten selten genug erscheinen, und wenn sie auftauchen, müssen Sie sie trotzdem vorsichtig lesen (weil sie complex sind). .

Nur zwei weitere Vorschläge, die ich hier nicht gesehen habe. Hoffe das hilft jemandem :)

1

Nur ein paar andere zufällige Ideen der Vollständigkeit halber. Wenn sie für Sie arbeiten, verwenden Sie sie. Ansonsten ist es wahrscheinlich besser, etwas anderes auszuprobieren.

Sie können dies auch mit einem Wörterbuch tun:

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

Diese Option ist komplizierter, kann aber auch nützlich sein:

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

Keine Ahnung, ob das für Sie funktioniert, aber es ist eine andere Option, die Sie in Betracht ziehen sollten. Hier ist noch ein Weg:

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'

Die letzten beiden habe ich nicht getestet, aber die Konzepte sollten ausreichen, um Sie zum Laufen zu bringen, wenn Sie damit einverstanden sind.

(Und wenn es sich nur um eine einmalige Sache handelt, ist es wahrscheinlich besser, wenn Sie die Methode anwenden, die Sie zuerst vorgestellt haben. Wenn Sie den Vergleich an vielen Stellen durchführen, können diese Methoden die Lesbarkeit ausreichend verbessern du fühlst dich nicht so schlecht über die Tatsache, dass sie irgendwie hackig sind.)

1
Jason Baker

Sie könnten es in zwei Zeilen aufteilen

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

Oder fügen Sie sogar eine Bedingung zu einer Zeit hinzu. Auf diese Weise wird zumindest das Durcheinander von if getrennt.

1
SarcasticSully

Ich weiß, dass dieser Thread alt ist, aber ich habe Python 2.7-Code und PyCharm (4.5) klagt immer noch über diesen Fall:

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

Selbst bei der PEP8-Warnung "visuell eingerückte Zeile mit dem gleichen Einzug als nächste logische Zeile" ist der tatsächliche Code vollständig in Ordnung? Es ist nicht "zu stark eingerückt"?

... manchmal wünschte ich mir, Python hätte die Kugel gebissen und wäre nur mit geschweiften Klammern gegangen. Ich frage mich, wie viele Fehler im Laufe der Jahre versehentlich durch versehentliches Fehlen von Eindrücken ausgelöst wurden ...

1
SMGreenfield

Ich denke, @ zkandas Lösung wäre gut mit einer kleinen Wendung. Wenn Sie Ihre Bedingungen und Werte in ihren eigenen jeweiligen Listen hätten, könnten Sie den Vergleich anhand eines Listenverständnisses durchführen, wodurch sich das Hinzufügen von Bedingungs-Wert-Paaren etwas allgemeiner gestaltet.

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

Wenn ich eine Aussage wie diese hart codieren möchte, würde ich sie aus Gründen der Lesbarkeit so schreiben:

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

Und nur um eine andere Lösung mit einem iand-Operator zu werfen :

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

if proceed:
    # do something
1
ryanjdillon

wenn unsere if & eine else-Bedingung mehrere Anweisungen in ihr ausführen muss, können wir wie folgt schreiben. Jedes Mal, wenn wir ein if-else-Beispiel mit einer Anweisung darin haben.

Danke, dass es für mich funktioniert.

#!/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

Ich finde, wenn ich lange Bedingungen habe, habe ich oft einen kurzen Code-Body. In diesem Fall habe ich den Körper doppelt eingerückt, also:

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

Ich verwende normalerweise: 

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

Packen Sie Ihre Bedingungen in eine Liste und tun Sie dann smth. mögen:

if False not in Conditions:
    do_something
0
psihodelia

Alle Befragten, die auch mehrere Bedingungen für die if-Anweisung angeben, sind genauso hässlich wie das dargestellte Problem. Sie lösen dieses Problem nicht, indem Sie dasselbe tun. 

Sogar die Antwort von PEP 0008 ist abstoßend.

Hier ist ein viel lesbarerer Ansatz

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

Soll ich meine Worte essen? Überzeugen Sie mich, dass Sie Multi-Conditionals benötigen und ich werde das buchstäblich ausdrucken und zu Ihrer Unterhaltung essen.

0
Stoff

Hier ist ein anderer Ansatz:

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

Dadurch können Sie auch leicht eine weitere Bedingung hinzufügen, ohne die if -Anweisung zu ändern, indem Sie einfach eine weitere Bedingung an die Liste anhängen:

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

Ich denke, so etwas ist die am besten lesbare Option:

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

oder wenn dies klarer ist:

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

Es gibt keinen Grund, in dem der Einzug ein Vielfaches von 4 sein sollte, z. siehe "Ausrichten mit öffnendem Trennzeichen":

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

0
Dima Tisnek