Python: any() / all() 中的惰性函数求值
Python: Lazy Function Evaluation in any() / all()
Python 中的逻辑运算符是惰性的。定义如下:
def func(s):
print(s)
return True
调用 or
运算符
>>> func('s') or func('t')
's'
只计算第一个函数调用,因为 or
认识到表达式计算为
True
,与第二个函数调用的 return 值无关。 and
确实有类似的行为。
但是,当按以下方式使用 any()
(类比:all()
)时:
>>> any([func('s'), func('t')])
's'
't'
所有函数调用都会被求值,因为在 any
开始迭代其项目的布尔值之前,首先构造内部列表。当我们省略列表构造而只写
时,也会发生同样的情况
>>> 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'))]
)
这样,一旦生成器中的一个函数调用求值为 True
,any
将停止调用生成器中的 next()
元素,这意味着函数求值完全懒惰。
wjandrea in a comment: We can also to use lambda expressions 提到了另一种推迟函数求值的巧妙方法,例如:
>>> any(f() for f in [lambda: func('s'), lambda: func('t')]
's'
Python 中的逻辑运算符是惰性的。定义如下:
def func(s):
print(s)
return True
调用 or
运算符
>>> func('s') or func('t')
's'
只计算第一个函数调用,因为 or
认识到表达式计算为
True
,与第二个函数调用的 return 值无关。 and
确实有类似的行为。
但是,当按以下方式使用 any()
(类比:all()
)时:
>>> any([func('s'), func('t')])
's'
't'
所有函数调用都会被求值,因为在 any
开始迭代其项目的布尔值之前,首先构造内部列表。当我们省略列表构造而只写
>>> 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'))]
)
这样,一旦生成器中的一个函数调用求值为 True
,any
将停止调用生成器中的 next()
元素,这意味着函数求值完全懒惰。
wjandrea in a comment: We can also to use lambda expressions 提到了另一种推迟函数求值的巧妙方法,例如:
>>> any(f() for f in [lambda: func('s'), lambda: func('t')]
's'