代码之家  ›  专栏  ›  技术社区  ›  Brendan Long

python的reduce()是否短路?

  •  13
  • Brendan Long  · 技术社区  · 14 年前

    如果我这样做:

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

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

    类似地:

    result = reduce(operator.or_, [True] * 1000)
    
    5 回复  |  直到 6 年前
        1
  •  21
  •   Muhammad Alkarouri    14 年前

    没有,你的选择是 any all .

    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
    
        2
  •  5
  •   kindall    14 年前

    reduce()不仅不短路,也不可能对所有正在减少的项短路,因为它一次只考虑两个项。此外,它不知道使用该功能时的短路情况。(如果函数可以有一个属性来指示它们开始短路的值,那么这就非常好了,reduce()可以识别并使用该属性,但它们没有。)

        3
  •  3
  •   Yauhen Yakimovich    13 年前

    很有可能(见 fate of 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)
    

    正如你看到的 减少 被打破成 i应用 & 艾比 .

    请注意,它不等于

    def ireduce(seq, func):
        prev = None
        for item in seq:
            if prev is None:
                prev = item
            else:
                prev = func(prev, item)
        return prev
    
        4
  •  2
  •   griswolf    13 年前

    记住,短路评估并不总是你想要的。”因此,修理“减少到短路”是一个错误。例如,在处理Django中的表单列表时,我最近不得不更改all()to reduce()的用法:我要报告所有的is-valid()问题,而不仅仅是第一个问题。

        5
  •  1
  •   Vikas    6 年前

    我有一个相关的用例,我想要一个不同于 any all 但相当于循环 or and . 解决方案是使用 filter 而不是 reduce .

    任何 返回布尔值,而 返回最后一个计算为的对象 True .

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

    返回最后一个计算为的对象 .

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

    reduce(operator.or_, xs) 不会短路,但是 next(filter(bool, xs)) 在Python 3或 next(itertools.ifilter(bool, xs)) 在python2中会短路。不是因为 滤波器 短路,但因为迭代器返回的是惰性的,并且只在需要时进行计算。通过使用 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
    

    结果不如 任何 但够近了

    >>> %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)