有没有Python习惯用法实现短路求值的函数/表达式列表?
我写了一个简单的脚本来解决一个“逻辑谜题”,这种谜题就像我们在学校时遇到的那种,你会得到一些规则,然后需要找出解决方案,比如“有五个音乐家,分别叫A、B、C、D和E,他们在一个音乐会上依次演奏……如果A在B之前,D不是最后一个……那他们演奏的顺序是什么?”等等。
为了评估可能的解决方案,我把每个“规则”写成一个单独的函数,这些函数会检查一个可能的解决方案(简单来说就是一个字符串列表)是否有效,比如:
#Fifth slot must be B or D
def rule1(solution):
return solution[4] == 'B' or solution[4] == 'D'
#There must be at least two spots between A and B
def rule2(solution):
returns abs(solution.index('A') - solution.index('B')) >= 2
#etc...
我想找到一种更符合Python风格的方法来测试一个可能的解决方案是否符合所有这些规则,并且在第一个规则失败后能够停止评估。
一开始,我写了最简单的代码:
def is_valid(solution):
return rule1(solution) and rule2(solution) and rule3(solution) and ...
但这样看起来有点丑。我想也许可以用列表推导式让代码看起来更优雅一些……
def is_valid(solution)
rules = [rule1, rule2, rule3, rule4, ... ]
return all([r(solution) for f in rules])
……但后来我意识到,由于列表推导式是在all()
函数评估之前生成的,这样会导致所有规则都会被评估,即使第一个返回了False
。
所以我的问题是:有没有一种更符合Python风格或函数式的方法,可以评估一系列True
/False
的表达式,并且能够短路处理,而不需要写出一长串return f1(s) and f2(s) and f3(s) ...
?
1 个回答
使用一个生成器表达式:
rules = [ rule1, rule2, rule3, rule4, ... ]
rules_generator = ( r( solution ) for r in rules )
return all( rules_generator )
语法糖:你可以省略多余的括号:
rules = [ rule1, rule2, rule3, rule4, ... ]
return all( r( solution ) for r in rules )
生成器(generator)基本上是一个有.next()
方法的对象,这个方法会返回某个可迭代对象中的下一个项目。这意味着它们可以做一些很有用的事情,比如分块读取文件,而不是一次性把整个文件都加载到内存中,或者可以遍历非常大的整数。你可以用for
循环来遍历它们,Python会在后台处理这些事情。例如,range
在Python 3中就是一个生成器。
你可以通过在函数定义中使用yield
语句来创建自己的自定义生成器表达式,而不是使用return
:
def integers():
i = 0
while True:
yield i
Python会处理保存函数的状态等等。这些真是太棒了!