Python的reduce()短路吗?

时间:2010-08-25 22:09:51

标签: python

如果我这样做:

result = reduce(operator.and_, [False] * 1000)

第一个结果后它会停止吗? (自False & anything == False

类似地:

result = reduce(operator.or_, [True] * 1000)

6 个答案:

答案 0 :(得分:21)

没有。在这种情况下,您的替代方案是anyall

result = reduce(operator.and_, [False] * 1000)
result = reduce(operator.or_, [True] * 1000)

可以替换为

result = all([False] * 1000)
result = any([True] * 1000)

做短路。

时间结果显示出差异:

In [1]: import operator

In [2]: timeit result = reduce(operator.and_, [False] * 1000)
10000 loops, best of 3: 113 us per loop

In [3]: timeit result = all([False] * 1000)
100000 loops, best of 3: 5.59 us per loop

In [4]: timeit result = reduce(operator.or_, [True] * 1000)
10000 loops, best of 3: 113 us per loop

In [5]: timeit result = any([True] * 1000)
100000 loops, best of 3: 5.49 us per loop

答案 1 :(得分:5)

reduce()不仅不会短路,它不可能在所有减少的项目上短路,因为它一次只考虑两个项目。另外,它不知道所使用的功能短路的条件。 (如果函数可以有一个属性来指示它们开始短路的值,那么reduce()可以识别并使用它,但它们不会。)

答案 2 :(得分:3)

很可能(参见fate of reduce)替代 reduce 实施将做得很好。

这个想法对我来说非常有用,可以让设计更加透明。

def ipairs(seq):
    prev = None
    for item in seq:
        if prev is not None:
            yield (prev, item)
        prev = item

def iapply(seq, func):
    for a, b in ipairs(seq):
        yield func(a, b)

def satisfy(seq, cond):
    return all(iapply(seq, cond))

def is_uniform(seq):
    return satisfy(seq, lambda a, b: a == b)

如您所见 reduce 分为 iapply < - ipairs

请注意,它不等同于

def ireduce(seq, func):
    prev = None
    for item in seq:
        if prev is None:
            prev = item
        else:
            prev = func(prev, item)
    return prev

答案 3 :(得分:2)

请记住,短路评估并不总是您想要的。出于这个原因,“修复”减少到短路将是一个错误。例如,我最近不得不在处理django中的表单列表时将all()的使用更改为reduce():我想报告任何is_valid()问题,而不仅仅是第一个问题。

答案 4 :(得分:1)

我有一个相关的用例,其中我想要一种不同于anyall的行为,但等效于orand上的循环。解决方案是使用filter而不是reduce

any返回布尔值,而or返回最后评估为True的对象。

>>> any(['', 'a'])
True
>>> any(['', ''])
False
>>> any([0, 1])
True
>>> any([0, 0])
False

or返回最后一个评估为True的对象。

>>> '' or 'a'
'a'
>>> '' or ''
''
>>> 0 or 1
1
>>> 0 or 0
0

reduce(operator.or_, xs)不会短路,但是python3中的next(filter(bool, xs))或python2中的next(itertools.ifilter(bool, xs))会短路。这不是因为filter短路,而是因为返回的迭代器是惰性的,并且仅在需要时才进行评估。通过使用next,我们仅要求满足过滤条件的第一个元素。

>>> def maybenext(iter, onstopiter=False):
...     try: return  next(iter)
...     except StopIteration: return onstopiter
...     
>>> 
>>> maybenext(filter(bool, ['', 'a']))
'a'
>>> maybenext(filter(bool, ['', '']))
False
>>> maybenext(filter(bool, [0, 1]))
1
>>> maybenext(filter(bool, [0, 0]))
False

结果不如any快但足够接近

>>> %timeit maybenext(filter(bool, [1] * 1000))
2.48 µs ± 91.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit any([1] * 1000)
2.26 µs ± 90.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit reduce(operator.or_, [1] * 1000)
47.3 µs ± 1.75 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

答案 5 :(得分:0)

这是一个可能的懒惰化约实现:

from itertools import accumulate

def lazy_reduce(pred, reducer, it, initial=None):
    accum = accumulate(it, reducer, initial=initial)
    last = None
    for item in accum:
        last = item
        if pred(item):
            return item
    return last

这应该像reduce一样工作,但会缩短pred

from operator import add
from itertools import count
res = short_reduce(lambda x: x > 20, add, count(1,8)) # 27