Sometimes I break long conditions in ifs onto several lines. The most obvious way to do this is:
if (cond1 == 'val1' and cond2 == 'val2' and
cond3 == 'val3' and cond4 == 'val4'):
do_something
Isn’t very very appealing visually, because the action blends with the conditions. However, it is the natural way using correct Python indentation of 4 spaces.
For the moment I’m using:
if ( cond1 == 'val1' and cond2 == 'val2' and
cond3 == 'val3' and cond4 == 'val4'):
do_something
You don’t need to use 4 spaces on your second conditional line. Maybe use:
if (cond1 == 'val1' and cond2 == 'val2' and
cond3 == 'val3' and cond4 == 'val4'):
do_something
Also, don’t forget the whitespace is more flexible than you might think:
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
Both of those are fairly ugly though.
Maybe lose the brackets (the Style Guide discourages this though)?
if cond1 == 'val1' and cond2 == 'val2' and \
cond3 == 'val3' and cond4 == 'val4':
do_something
This at least gives you some differentiation.
Or even:
if cond1 == 'val1' and cond2 == 'val2' and \
cond3 == 'val3' and \
cond4 == 'val4':
do_something
I think I prefer:
if cond1 == 'val1' and \
cond2 == 'val2' and \
cond3 == 'val3' and \
cond4 == 'val4':
do_something
Here’s the Style Guide, which (since 2010) recommends using brackets.
Here’s my very personal take: long conditions are (in my view) a code smell that suggests refactoring into a boolean-returning function/method. For example:
def is_action__required(...):
return (cond1 == 'val1' and cond2 == 'val2'
and cond3 == 'val3' and cond4 == 'val4')
Now, if I found a way to make multi-line conditions look good, I would probably find myself content with having them and skip the refactoring.
On the other hand, having them perturb my aesthetic sense acts as an incentive for refactoring.
My conclusion, therefore, is that multiple line conditions should look ugly and this is an incentive to avoid them.
I suggest moving the and keyword to the second line and indenting all lines containing conditions with two spaces instead of four:
if (cond1 == 'val1' and cond2 == 'val2'
and cond3 == 'val3' and cond4 == 'val4'):
do_something
This is exactly how I solve this problem in my code. Having a keyword as the first word in the line makes the condition a lot more readable, and reducing the number of spaces further distinguishes condition from action.
# 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()
It seems worth quoting PEP 0008 (Python’s official style guide), since it comments upon this issue at modest length:
When the conditional part of an if -statement is long enough to require that it be written across multiple lines, it’s worth noting that the combination of a two character keyword (i.e. if ), plus a single space, plus an opening parenthesis creates a natural 4-space indent for the subsequent lines of the multiline conditional. This can produce a visual conflict with the indented suite of code nested inside the if -statement, which would also naturally be indented to 4 spaces. This PEP takes no explicit position on how (or whether) to further visually distinguish such conditional lines from the nested suite inside the if -statement. Acceptable options in this situation include, but are not limited to:
# 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()
Note the “not limited to” in the quote above; besides the approaches suggested in the style guide, some of the ones suggested in other answers to this question are acceptable too.
Adding to what @krawyoti said… Long conditions smell because they are difficult to read and difficult to understand. Using a function or a variable makes the code clearer. In Python, I prefer to use vertical space, enclose parenthesis, and place the logical operators at the beginning of each line so the expressions don’t look like “floating”.
conditions_met = (
cond1 == 'val1'
and cond2 == 'val2'
and cond3 == 'val3'
and cond4 == 'val4'
)
if conditions_met:
do_something
If the conditions need to be evaluated more than once, as in a while loop, then using a local function is best.
Personally, I like to add meaning to long if-statements. I would have to search through code to find an appropriate example, but here’s the first example that comes to mind: let’s say I happen to run into some quirky logic where I want to display a certain page depending on many variables.
English: “If the logged-in user is NOT an administrator teacher, but is just a regular teacher, and is not a student themselves…”
if not user.isAdmin() and user.isTeacher() and not user.isStudent():
doSomething()
Sure this might look fine, but reading those if statements is a lot of work. How about we assign the logic to label that makes sense. The “label” is actually the variable name:
displayTeacherPanel = not user.isAdmin() and user.isTeacher() and not user.isStudent()
if displayTeacherPanel:
showTeacherPanel()
This may seem silly, but you might have yet another condition where you ONLY want to display another item if, and only if, you’re displaying the teacher panel OR if the user has access to that other specific panel by default:
if displayTeacherPanel or user.canSeeSpecialPanel():
showSpecialPanel()
Try writing the above condition without using variables to store and label your logic, and not only do you end up with a very messy, hard-to-read logical statement, but you also just repeated yourself. While there are reasonable exceptions, remember: Don’t Repeat Yourself (DRY).
回答 12
“ all”和“ any”对于相同类型案例的许多条件都很好。但是他们总是评估所有条件。如本例所示:
def c1():print" Executed c1"returnFalsedef c2():print" Executed c2"returnFalseprint"simple and (aborts early!)"if c1()and c2():passprintprint"all (executes all :( )"if all((c1(),c2())):passprint
(I’ve lightly modified the identifiers as fixed-width names aren’t representative of real code – at least not real code that I encounter – and will belie an example’s readability.)
if (cond1 == "val1" and cond22 == "val2"
and cond333 == "val3" and cond4444 == "val4"):
do_something
This works well for “and” and “or” (it’s important that they’re first on the second line), but much less so for other long conditions. Fortunately, the former seem to be the more common case while the latter are often easily rewritten with a temporary variable. (It’s usually not hard, but it can be difficult or much less obvious/readable to preserve the short-circuiting of “and”/”or” when rewriting.)
Since I found this question from your blog post about C++, I’ll include that my C++ style is identical:
if (cond1 == "val1" and cond22 == "val2"
and cond333 == "val3" and cond4444 == "val4") {
do_something
}
# 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!")# orif all('substring'in item
for item in my_list
):print("Hello World!")
In recent times I have been preferring the all and any functions, since I rarely mix And and Or comparisons this works well, and has the additional advantage of Failing Early with generators comprehension:
if all([
cond1,
cond2,
]):
print("Hello World!")
Just remember to pass in a single iterable! Passing in N-arguments is not correct.
Note: any is like many or comparisons, all is like many and comparisons.
This combines nicely with generator comprehensions, for example:
# 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!")
All respondents that also provide multi-conditionals for the if statement is just as ugly as the problem presented. You don’t solve this problem by doing the same thing..
Even the PEP 0008 answer is repulsive.
Here is a far more readable approach
condition = random.randint(0, 100) # to demonstrate
anti_conditions = [42, 67, 12]
if condition not in anti_conditions:
pass
Want me to eat my words? Convince me you need multi-conditionals and I’ll literally print this and eat it for your amusement.
I think @zkanda’s solution would be good with a minor twist. If you had your conditions and values in their own respective lists, you could use a list comprehension to do the comparison, which would make things a bit more general for adding condition/value pairs.
conditions = [1, 2, 3, 4]
values = [1, 2, 3, 4]
if all([c==v for c, v in zip(conditions, values)]):
# do something
If I did want to hard-code a statement like this, I would write it like this for legibility:
if (condition1==value1) and (condition2==value2) and \
(condition3==value3) and (condition4==value4):
And just to throw another solution out there with an iand operator:
proceed = True
for c, v in zip(conditions, values):
proceed &= c==v
if proceed:
# do something
回答 19
出于完整性考虑,仅提供了一些其他随机想法。如果它们对您有用,请使用它们。否则,您最好尝试其他方法。
您也可以使用字典来做到这一点:
>>> x ={'cond1':'val1','cond2':'val2'}>>> y ={'cond1':'val1','cond2':'val2'}>>> x == y
True
这个选项比较复杂,但是您可能还会发现它有用:
classKlass(object):def __init__(self, some_vars):#initialize conditions heredef __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!"
邓诺(Dunno)是否适合您,但这是您可以考虑的另一种选择。这是另一种方式:
classKlass(object):def __init__(self):#initialize conditions heredef __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'
Just a few other random ideas for completeness’s sake. If they work for you, use them. Otherwise, you’re probably better off trying something else.
You could also do this with a dictionary:
>>> x = {'cond1' : 'val1', 'cond2' : 'val2'}
>>> y = {'cond1' : 'val1', 'cond2' : 'val2'}
>>> x == y
True
This option is more complicated, but you may also find it useful:
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!"
Dunno if that works for you, but it’s another option to consider. Here’s one more way:
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'
The last two I haven’t tested, but the concepts should be enough to get you going if that’s what you want to go with.
(And for the record, if this is just a one time thing, you’re probably just better off using the method you presented at first. If you’re doing the comparison in lots of places, these methods may enhance readability enough to make you not feel so bad about the fact that they are kind of hacky.)
I’ve been struggling to find a decent way to do this as well, so I just came up with an idea (not a silver bullet, since this is mainly a matter of taste).
if bool(condition1 and
condition2 and
...
conditionN):
foo()
bar()
I find a few merits in this solution compared to others I’ve seen, namely, you get exactly an extra 4 spaces of indentation (bool), allowing all conditions to line up vertically, and the body of the if statement can be indented in a clear(ish) way. This also keeps the benefits of short-circuit evaluation of boolean operators, but of course adds the overhead of a function call that basically does nothing. You could argue (validly) that any function returning its argument could be used here instead of bool, but like I said, it’s just an idea and it’s ultimately a matter of taste.
Funny enough, as I was writing this and thinking about the “problem”, I came up with yet another idea, which removes the overhead of a function call. Why not indicate that we’re about to enter a complex condition by using extra pairs of parentheses? Say, 2 more, to give a nice 2 space indent of the sub-conditions relative to the body of the if statement. Example:
if (((foo and
bar and
frob and
ninja_bear))):
do_stuff()
I kind of like this because when you look at it, a bell immediatelly rings in your head saying “hey, there’s a complex thing going on here!”. Yes, I know that parentheses don’t help readability, but these conditions should appear rarely enough, and when they do show up, you are going to have to stop and read them carefuly anyway (because they’re complex).
Anyway, just two more proposals that I haven’t seen here. Hope this helps someone :)
回答 21
您可以将其分为两行
total = cond1 =='val'and cond2 =='val2'and cond3 =='val3'and cond4 == val4
if total:
do_something()
I know this thread is old, but I have some Python 2.7 code and PyCharm (4.5) still complains about this case:
if foo is not None:
if (cond1 == 'val1' and cond2 == 'val2' and
cond3 == 'val3' and cond4 == 'val4'):
# some comment about do_something
do_something
Even with the PEP8 warning “visually indented line with same indent as next logical line”, the actual code is completely OK? It’s not “over-indenting?”
…there are times I wish Python would’ve bit the bullet and just gone with curly braces. I wonder how many bugs have been accidentally introduced over the years due to accidental mis-indentation…
if our if & an else condition has to execute multiple statement inside of it than we can write like below.
Every when we have if else example with one statement inside of it .
param_Val01 =Value01#give a meaningful name for param_Val(i) preferable an integer
param_Val02 =Value02
param_Val03 =Value03
param_Val04 =Value04# and ... etc
conditions =0# this is a value placeholder########Add script that if true will make:
conditions = conditions + param_Val01 #value of placeholder is updated########### repeat as neededif conditions = param_Val01 + param_Val02 + param_Val03 + param_Val04:do something
Pardon my noobness, but it happens that I’m not as knowledgeable of #Python as anyone of you here, but it happens that I have found something similar when scripting my own objects in a 3D BIM modeling, so I will adapt my algorithm to that of python.
The problem that I find here, is double sided:
Values my seem foreign for someone who may try to decipher the script.
Code maintenance will come at a high cost, if those values are changed (most probable), or if new conditions must be added (broken schema)
Do to bypass all these problems, your script must go like this
param_Val01 = Value 01 #give a meaningful name for param_Val(i) preferable an integer
param_Val02 = Value 02
param_Val03 = Value 03
param_Val04 = Value 04 # and ... etc
conditions = 0 # this is a value placeholder
########
Add script that if true will make:
conditions = conditions + param_Val01 #value of placeholder is updated
########
### repeat as needed
if conditions = param_Val01 + param_Val02 + param_Val03 + param_Val04:
do something
Pros of this method:
Script is readable.
Script can be easy maintained.
conditions is a 1 comparison operation to a sum of values that represents the desired conditions.