Python';短路?
如果我这样做:Python';短路?,python,Python,如果我这样做: result = reduce(operator.and_, [False] * 1000) 它会在第一个结果之后停止吗?(因为False&anything==False) 同样地: result = reduce(operator.or_, [True] * 1000) 没有。在这种情况下,您的选择是和 可以用 result = all([False] * 1000) result = any([True] * 1000) 哪一个会短路 计时结果显示了差异: In [1]
result = reduce(operator.and_, [False] * 1000)
它会在第一个结果之后停止吗?(因为False&anything==False
)
同样地:
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
reduce()不仅不会短路,而且不可能对所有要减少的项短路,因为它一次只考虑两个项。此外,它不知道在什么条件下使用的功能短路。(如果函数可以有一个属性来指示它们开始短路的值,这将是一件好事,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请记住,短路评估并不总是您想要的。因此,“修复”减少到短路将是一个错误。例如,在django中处理表单列表时,我最近不得不将all()的用法改为reduce():我想报告任何is_valid()问题,不仅仅是第一个。我有一个相关的用例,我想要一个不同于
任何
和所有
的行为,但相当于一个循环或和和
。解决方案是使用过滤器
,而不是减少
any
返回布尔值,而或
返回最后一个计算为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
或
返回最后一个计算为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)
不会短路,但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)
下面是一个可能的延迟reduce实现:
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
你说得对any()
和all()
似乎正是我所需要的(而且可能更清晰)。您是如何进行计时的?我正在使用ipython
及其timeit
命令。但是Python有一个timeit模块。因此,您可以从命令行执行python-mtimeit“result=any([True]*10)”
计时。它可以使用一个关键字参数作为短路条件,其他内置函数做:reduce(operator.or,mylist,key=lambda x::x<0)这就是Lisp用户如此喜欢Lisp的原因——因为所有函数都是数据,它(原则上)是可以进行这种检测。