Python 中的逻辑运算符是惰性的。具有以下定义:
def func(s):
print(s)
return True
调用or
运算符(operator)>>> func('s') or func('t')
's'
只计算第一个函数调用,因为 or
识别出表达式的计算结果为True
,无论第二个函数调用的返回值如何。 and
确实表现类似。但是,当使用
any()
(类似地: all()
)以下列方式:>>> any([func('s'), func('t')])
's'
't'
所有函数调用都被评估,因为内部列表首先被构造,在 any
之前开始迭代其项目的 bool 值。当我们省略列表构造而只写时也会发生同样的情况>>> any(func('s'), func('t'))
's'
't'
这样我们就失去了 any
的力量。短路,这意味着一旦可迭代的第一个元素为真,它就会中断。如果函数调用很昂贵,那么预先评估所有函数是一个很大的损失,并且是对 any
的这种能力的浪费。 .在某种意义上,我们可以称之为 Python 陷阱,因为对于试图利用 any
的这一特性的用户来说,这可能是出乎意料的。 , 因为 any
通常被认为只是链接 or
序列的另一种语法方式。陈述。但是any
只是短路,不是偷懒,这就是这里的区别。any
is accepting an iterable .因此,应该有一种方法可以创建一个迭代器,它不会预先评估其元素,而是将它们未评估的元素传递给 any
。并让他们评估 any
内部只是,为了实现完全惰性的评估。所以,问题是:我们如何使用
any
真正的惰性函数评估?这意味着:我们如何创建 any
的函数调用的迭代器?可以消费,而无需提前评估所有函数调用?
最佳答案
我们可以使用 generator expression ,分别传递函数及其参数并仅在生成器中进行评估,如下所示:
>>> any(func(arg) for arg in ('s', 't'))
's'
对于具有不同签名的不同函数,这可能如下所示:any(
f(*args)
for f, args in [(func1, ('s',)), (func2, (1, 't'))]
)
这样,any
将停止调用 next()
一旦生成器中的一个函数调用计算为 True
,生成器中的元素,这意味着函数评估是完全惰性的。wjandrea 提到了另一种推迟函数评估的巧妙方法。在评论中:我们也可以使用lambda expressions ,像这样:
>>> any(f() for f in [lambda: func('s'), lambda: func('t')]
's'
关于Python:any()/all() 中的惰性函数求值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64090762/