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

问题描述 投票:0回答:2

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
作为短路的力量,这意味着一旦可迭代的第一个元素为真,它就会中断。如果函数调用的成本很高,那么预先评估所有函数就会造成很大的损失,并且浪费了
any
的这种能力。从某种意义上说,人们可以将其称为 Python 陷阱,因为对于尝试利用
any
的此功能的用户来说可能会出乎意料,而且因为
any
通常被认为是链接一系列
or 的另一种语法方式。 
陈述。但
any
只是短路,而不是懒惰,这就是这里的区别。

any
正在接受一个可迭代的。因此,应该有一种方法来创建一个迭代器,它不会预先评估其元素,而是将未评估的元素传递给
any
并让它们仅在
any
内部评估,以实现完全惰性评估。

所以,问题是:我们如何使用

any
进行真正的惰性函数求值?这意味着:我们如何制作一个
any
可以使用的函数调用迭代器,而不提前评估所有函数调用?

python lazy-evaluation any short-circuiting
2个回答
16
投票

我们可以使用生成器表达式,分别传递函数及其参数,并仅在生成器中进行计算,如下所示:

>>> 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表达式,如下所示:

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

0
投票

令人遗憾的是,any() 和 all() 没有逻辑功能,并且受到这种有点人为的约束的限制。相反,一次循环构造可以很方便,这也与早期返回的函数有关。

once-thru 循环可以是处理它的便捷方法,特别是在需要生成然后使用中间结果的情况下。

    for _ in range(1):
        val = func1()
        if not val:
            break
        val = intermediate_func1()
        if not func3(val):
            break
        val2 = intermediate_func2(val)
        if not func4(val2):
            break
        result = the_really_expensive_function(val, val2)
        if result:
            return True, result
    return False, None

使用函数和提前返回的类似构造。

def example():

    val = func1()
    if not val:
        return False, None
    val1 = intermediate_func1(val)
    if not func3(val1):
        return False, None
    val2 = intermediate_func2(val1)
    if not func4(val2):
        return False, None
    result = the_really_expensive_function(val, val2)
    if result:
        return True, result
    return False, None

我想使用什么(但不能;这仅适用于 := 运算符):

if all(
    val := func1(),
    func3(val1 := intermediate_func1(val)),
    func4(val2 := intermediate_func2(val)),
    result := the_really_expensive_function(val, val2),
    ):
    return result, True
return False, None

也许未来这是可行的。

--雷

© www.soinside.com 2019 - 2024. All rights reserved.