如何高效地将 or/and 操作应用于 python 列表中的所有元素



我知道reduce和all/any都可以解决问题,但是当列表很大时,它的性能很差。

例如:

定义一个函数,包括打印以检查该函数是否已执行

In [33]: def func(x):
   ....:     print x
   ....:     return bool(x)
   ....: 

传递operator.or_作为reduce函数

In [34]: import operator
In [35]: reduce(operator.or_, [func(1), func(0)])
1
0
Out[35]: True

然后我们发现第二个函数已经执行,即使第一个函数返回 True。

如果我直接使用 or 操作,一旦发现其中一个返回 True,它将立即返回。

In [36]: func(1) or func(0)
1
Out[36]: True

但是,如果我有一个很大的列表,我就不能这样做。

有什么优雅的方法可以做到这一点吗?或者我应该检查什么 for 循环?

更新

我用于任何的起源方式是

In [26]: any([func(1), func(0)])
1
0
Out[26]: True

它确实评估了所有功能。

通过@Martijn Pieters的回答,我现在知道我可能会以错误的方式使用它。很抱歉未清除。

any() 正是您在这里所需要的,并结合生成器表达式:

any(func(i) for i in big_list)

这将停止迭代第一个值,其中func(i)返回 true 值。一旦找到一个True值,你就证明了输入序列中有一个值是真的("没有任何值是真的?"——>是的,我们至少找到了一个)。

对于and,您将改用all()

all(func(i) for i in big_list)

它将False发现func(i) falsey 值的那一刻返回。如果找到一个假值,那么你已经证明至少有一个值不为真,所以它们不可能都是真的。

请注意,这两个函数被赋予一个生成器表达式:

(func(i) for i in big_list)

这是懒惰计算的;每次你请求生成器表达式的下一个值时,它都会计算循环并执行func(i)表达式一次。它不会一次生成整个列表,它会一个接一个地生成项目。

您的reduce(operator.or_, [func(1), func(0)])表达式必须先构建整个输入列表,然后才能调用reduce()reduce()方法将处理整个输入列表,它不会短路,因为它不知道对输入值应用了什么操作。您也可以reduce()提供一个生成器表达式,但是一旦设置了结果(or的第一个真值或and的第一个假值),它就不会停止迭代,同样是因为reduce()对正在执行的操作没有专业知识。

除了另一个答案之外,这个问题:

reduce(operator.or_, [func(1), func(0)])

是参数总是在调用函数之前被计算,因为 Python 不做惰性求值。使用迭代器(如Martijn的答案)可以避免这种情况,因为它根据需要生成列表,而不是一次生成所有列表。

最新更新