在if语句中设置多行条件的样式?

有时我会将if中的长条件分解为几行。最明显的方法是:

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

在视觉上不是很吸引人,因为动作与条件混合在一起。然而,这是使用4个空格的正确Python缩进的自然方式。

目前我正在使用:

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

但这不是很漂亮:-)

你能推荐另一种方法吗?

1512294 次浏览

这并没有改善太多,但…

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

您不需要在第二个条件行上使用4个空格。也许可以使用:

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

另外,不要忘记空格比你想象的更灵活:

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

不过,这两个都相当丑陋。

也许丢掉括号(风格指南不鼓励这样做)?

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

我想我更喜欢:

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

这是风格指南,它(自2010年以来)建议使用括号。

我建议将and关键字移动到第二行,并将所有包含条件的行缩进两个空格而不是四个:

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

这正是我在代码中解决这个问题的方式。将关键字作为行中的第一个单词使条件更具可读性,并且减少空格数量进一步区分条件和操作。

当我有一个非常大的if条件时,我更喜欢这种风格:

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

在退化的情况下,我采用了以下方法,它只是AND或OR。

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

它剃掉了几个字符,并清楚地表明这种情况没有微妙之处。

“all”和“any”对于同一类型case的许多条件都很好。但是他们总是评估所有条件。如下例所示:

def c1():print " Executed c1"return Falsedef 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

有人必须在这里支持使用垂直空格!:)

if (     cond1 == val1and cond2 == val2and cond3 == val3):do_stuff()

这使得每个条件都清晰可见。它还允许更清晰地表达更复杂的条件:

if (    cond1 == val1or(     cond2_1 == val2_1and cond2_2 >= val2_2and cond2_3 != bad2_3)):do_more_stuff()

是的,为了清晰起见,我们正在交易一些垂直房地产。在我看来,这是值得的。

只是为了完整起见的一些其他随机想法。如果它们对你有用,就使用它们。否则,你最好尝试其他东西。

你也可以用字典做到这一点:

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

此选项更复杂,但您可能也会发现它很有用:

class Klass(object):def __init__(self, some_vars):#initialize conditions heredef __nonzero__(self):return (self.cond1 == 'val1' and self.cond2 == 'val2' andself.cond3 == 'val3' and self.cond4 == 'val4')
foo = Klass()if foo:print "foo is true!"else:print "foo is false!"

不知道这是否适合你,但这是另一个可以考虑的选择。这里还有一个方法:

class Klass(object):def __init__(self):#initialize conditions heredef __eq__(self):return (self.cond1 == 'val1' and self.cond2 == 'val2' andself.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'

最后两个我还没有测试,但概念应该足以让你去,如果这是你想要的。

(为了记录在案,如果这只是一次性的事情,你可能最好使用你最初提出的方法。如果你在很多地方进行比较,这些方法可能会提高易读性,让你不会因为它们有点老套而感到很糟糕。)

如果我们只在条件和主体之间插入一个额外的空行,其余的以规范的方式执行呢?

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

附注:我总是使用制表符,而不是空格;我无法微调…

把你的条件打包成一个列表,然后做一些事情。比如:

if False not in Conditions:do_something

(我略微修改了标识符,因为固定宽度名称不代表真实代码-至少不是我遇到的真实代码-并且会掩盖示例的易读性。

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

这适用于“and”和“or”(它们在第二行的第一个很重要),但对于其他长条件来说就不那么适用了。幸运的是,前者似乎更常见,而后者通常很容易用临时变量重写。(重写时保持“and”/“or”的短路可能很困难,或者不太明显/可读。)

由于我从你的博客关于C++中找到了这个问题,我将包括我的C++风格是相同的:

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

这是我个人的看法:长条件(在我看来)是一种代码气味,建议重构为布尔返回函数/方法。例如:

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

现在,如果我找到了一种使多行条件看起来很好的方法,我可能会发现自己满足于拥有它们并跳过重构。

另一方面,让它们扰乱我的美感是重构的动力。

因此,我的结论是,多行条件应该看起来很丑陋,这是避免它们的动机。

我很惊讶没有看到我喜欢的解决方案,

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

由于and是一个关键字,它会被我的编辑器突出显示,并且看起来与下面的do_something完全不同。

添加到@Krawyoti所说的……长条件闻起来很难看,因为它们难以阅读和理解。使用函数或变量可以使代码更清晰。在Python中,我更喜欢使用垂直空格,用括号括起来,并将逻辑运算符放在每行的开头,这样表达式看起来就不会像“浮动”。

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

如果需要多次评估条件,例如在while循环中,那么最好使用本地函数。

我发现当我有长条件的时候,经常会出现短代码主体,那样的话,我直接对主体进行双缩进,这样:

if (cond1 == 'val1' and cond2 == 'val2' andcond3 == 'val3' and cond4 == 'val4'):do_something
  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

在这种情况下,没有理由缩进应该是4的倍数,例如参见“与开头分隔符对齐”:

就我个人而言,我喜欢为长的if语句添加意义。我必须在代码中搜索才能找到一个合适的例子,但这是我想到的第一个例子:假设我碰巧遇到一些奇怪的逻辑,我想根据许多变量显示某个页面。

中文:“如果登录用户不是管理员老师,而只是普通老师,本身不是学生……”

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

当然这看起来不错,但是阅读那些if语句是很多工作。我们将逻辑分配给有意义的标签怎么样?“标签”实际上是变量名:

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

这可能看起来很愚蠢,但您可能还有另一个条件,即您只想在以下情况下显示另一个项目:当且仅当您正在显示教师面板时,或者如果用户默认有权访问其他特定面板:

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

尝试编写上述条件而不使用变量来存储和标记你的逻辑,不仅你最终会得到一个非常混乱、难以阅读的逻辑语句,而且你也只是重复自己。虽然有合理的例外,但请记住:不要重复你自己(DRY)。

我通常做的是:

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

这样,闭合括号和结肠在视觉上标志着我们病情的结束。

这是另一种方法:

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

这也使得很容易添加另一个条件,而无需更改if语句,只需将另一个条件附加到列表中即可:

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

这就是我所做的,记住“all”和“any”接受可迭代,所以我只是在列表中放了一个长条件,让“all”完成工作。

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

简单明了,也通过了Pep8检查:

if (cond1 andcond2):print("Hello World!")

最近,我更喜欢allany函数,因为我很少混合和或比较,这效果很好,并且具有生成器理解早期失败的额外优势:

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

只要记住传入一个可迭代对象!传入N个参数是不正确的。

注意:any类似于许多or比较,all类似于许多and比较。


这与生成器理解很好地结合在一起,例如:

# 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 itemfor item in my_list):print("Hello World!")

更多:生成器理解

我一直在努力寻找一种合适的方法来做到这一点,所以我只是想出了一个主意(不是灵丹妙药,因为这主要是一个品味问题)。

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

与我见过的其他解决方案相比,我发现这个解决方案有一些优点,即,你可以额外获得4个缩进空格(bool),允许所有条件垂直对齐,并且if语句的主体可以以清晰(ish)的方式缩进。这也保留了布尔运算符短路评估的好处,但当然增加了基本上什么都不做的函数调用的开销。你可以(有效地)争辩说任何返回其参数的函数都可以在这里使用而不是bool,但就像我说的,这只是一个想法,最终是一个品味问题。

有趣的是,当我写这篇文章并思考“问题”时,我想出了又一个的想法,它消除了函数调用的开销。为什么不使用额外的括号来指示我们即将输入一个复杂的条件呢?比如说,再增加2个,相对于if语句的主体给出一个漂亮的2个子条件空格缩进。例子:

if (((foo andbar andfrob andninja_bear))):do_stuff()

我有点喜欢这个,因为当你看到它时,一个铃铛立即在你的脑海中响起,说“嘿,这里发生了一件复杂的事情!”。是的,我知道括号无助于易读性,但是这些条件应该很少出现,当它们出现时,你将不得不停下来仔细阅读它们(因为它们是复杂)。

不管怎样,还有两个我没有在这里看到的建议。希望这对某人有帮助:)

我通常使用:

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

似乎值得引用PEP 0008(Python的官方风格指南),因为它对这个问题的评论并不多:

if语句的条件部分足够长以至于需要跨多行编写时,值得注意的是,两个字符关键字(即if),加上一个空格,加上一个开括号的组合为多行条件的后续行创建了一个自然的4空格缩进。这可能会与嵌套在if语句中的缩进代码套件产生视觉冲突,该代码套件也会自然缩进到4个空格。本PEP没有明确说明如何(或是否)进一步直观地将这些条件行与if语句中的嵌套套件区分开来。这种情况下可接受的选项包括但不限于:

# No extra indentation.if (this_is_one_thing andthat_is_another_thing):do_something()
# Add a comment, which will provide some distinction in editors# supporting syntax highlighting.if (this_is_one_thing andthat_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_thingand that_is_another_thing):do_something()

注意上面引用中的“不限于”;除了风格指南中建议的方法之外,这个问题的其他答案中建议的一些方法也是可以接受的。

如果我们的if&an else条件必须在其中执行多个语句,那么我们可以像下面这样写。每一次,如果我们有一个例子,里面有一个语句。

谢谢你为我工作。

#!/usr/bin/pythonimport sysnumberOfArgument =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

你可以把它分成两条线

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

或者甚至一次添加一个条件。这样,至少它把混乱和if分开了。

我知道这个线程很旧,但我有一些Python 2.7代码,PyCharm(4.5)仍然抱怨这种情况:

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

即使PEP8警告“视觉上缩进的行与下一个逻辑行具有相同的缩进”,实际代码也完全正常?这不是“过度缩进?”

…有时我希望Python能咬紧牙关,只是用花括号离开。我想知道这些年来有多少错误由于意外的缩进错误而意外引入…

所有为if语句提供多条件的受访者都和提出的问题一样丑陋。你不能通过做同样的事情来解决这个问题。

即使是PEP 0008的答案也是令人厌恶的。

这里有一个更易读的方法

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

想让我吃我的话吗?说服我你需要多条件句,我会真的打印出来,然后吃了它供你娱乐。

我认为@zkanda的解决方案稍加改动会很好。如果你有自己的条件和值在各自的列表中,你可以使用列表理解来进行比较,这将使添加条件/值对的事情更加通用。

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

如果我真的想硬编码这样的语句,为了易读性,我会这样写:

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

然后抛出另一个解决方案#0运算符

proceed = Truefor c, v in zip(conditions, values):proceed &= c==v
if proceed:# do something

请原谅我的傲慢,但碰巧我对#Python的了解不如你们任何人,但碰巧我在3D BIM建模中编写自己的对象脚本时发现了类似的东西,所以我将调整我的算法以适应Python。

我在这里发现的问题是双面的:

  1. 对于那些试图破译剧本的人来说,我的价值观似乎很陌生。
  2. 如果更改了这些值(很可能),或者必须添加新条件(损坏的架构),代码维护将付出高昂的成本

要绕过所有这些问题,您的脚本必须像这样

param_Val01 = Value 01   #give a meaningful name for param_Val(i) preferable an integerparam_Val02 = Value 02param_Val03 = Value 03param_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

此方法的优点:

  1. 脚本是可读的。

  2. 脚本可以轻松维护。

  3. 条件是与表示所需条件的值之和的1比较操作。
  4. 无需多级条件

希望对大家有帮助