如果我这样做:
result = reduce(operator.and_, [False] * 1000)
第一个结果后它会停止吗? (自False & anything == False
)
类似地:
result = reduce(operator.or_, [True] * 1000)
答案 0 :(得分:21)
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)
我有一个相关的用例,其中我想要一种不同于any
和all
的行为,但等效于or
和and
上的循环。解决方案是使用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