Python:any()/all()中的惰性函数求值

2024-06-13 20:48:01 发布

您现在位置:Python中文网/ 问答频道 /正文

Python中的逻辑运算符是惰性的。定义如下:

def func(s):
    print(s)
    return True

调用or运算符

>>> func('s') or func('t')
's'

仅计算第一个函数调用,因为or识别表达式的计算结果为 True,与第二个函数调用的返回值无关and的行为与此类似

但是,当以以下方式使用any()(类似地:all())时:

>>> any([func('s'), func('t')])
's'
't'

所有函数调用都会求值,因为内部列表是在any开始迭代其项的布尔值之前首先构造的。当我们省略列表结构而只写

>>> any(func('s'), func('t'))
's'
't'

这样我们就失去了any短路的能力,这意味着一旦iterable的第一个元素是真的,它就会断开。如果函数调用很昂贵,那么预先评估所有函数将是一个巨大的损失,并且浪费了any的这种能力。从某种意义上说,我们可以称之为Python gotcha,因为对于试图利用any这一特性的用户来说,这可能是意外的,而且any通常被认为只是链接or语句序列的另一种语法方式。但是any只是短路,而不是懒惰,这就是这里的区别

^{} is accepting an iterable。因此,应该有一种创建迭代器的方法,该迭代器不预先计算其元素,而是将未计算的元素传递给any,只允许它们在any内部进行计算,以实现完全惰性的计算

因此,问题是:我们如何将any用于真正的惰性函数求值?这意味着:我们如何能够在不预先计算所有函数调用的情况下,生成any可以使用的函数调用迭代器


Tags: or函数true元素列表方式any运算符
1条回答
网友
1楼 · 发布于 2024-06-13 20:48:01

我们可以使用generator expression,分别传递函数及其参数,并仅在生成器中求值,如下所示:

>>> any(func(arg) for arg in ('s', 't'))
's'

对于具有不同签名的不同函数,这可能如下所示:

any(
    f(*args)
    for f, args in [(func1, ('s',)), (func2, (1, 't'))]
)

这样,any将在生成器中的一个函数调用求值为True时停止调用生成器中的next()元素,这意味着函数求值是完全惰性的

wjandrea在评论中提到了另一种推迟函数评估的简洁方法:我们也可以使用lambda expressions,如下所示:

>>> any(f() for f in [lambda: func('s'), lambda: func('t')]
's'

相关问题 更多 >