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

标签 python lazy-evaluation any short-circuiting

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/

相关文章:

python - 如何生成其中出现另一个字符串的字符串列表

python - 安装tensorflow-gpu时出现错误: Cannot uninstall 'wrapt' .~=1.14

ios - Swift 中的惰性属性等同于 Objective C 中的惰性 Init getter

Scala 惰性集合增长

haskell - Haskell 可以评估列表中的随机索引而不是垃圾收集吗?

python - 将 'any' 函数与列表压缩或生成器一起使用的正确语法

python - Python 函数中输出列表元素 "any"

python - Twitter API - 如何获取 OAUTH_FILE?

python - Pandas:将 DataFrame 转换为每个单元格的均值和标准差

python - 转换 DataFrame 中的每个组