如何在Python中以更高效的方式重写这个循环

1 投票
4 回答
896 浏览
提问于 2025-04-15 14:32

我有一个这样的循环:

a = range(10)
b = [something]
for i in range(len(a)-1):
    b.append(someFunction(b[-1], a[i], a[i+1]))

不过,这个for循环让性能下降很多。我尝试写一个窗口生成器,每次给我两个元素,但最后还是需要明确的for循环。有没有办法让这个过程更简洁、更高效,符合Python的风格呢?

谢谢

补充:我忘了提到b中的元素……抱歉大家。不过,我之前问题的解决方案对我其他问题也很有帮助。谢谢。

4 个回答

2

总会有某种循环存在,但有一种方法可能会减少开销:

import itertools

def generate(a, item):
  a1, a2 = itertools.tee(a)
  next(a2)
  for x1, x2 in itertools.izip(a1, a2):
    item = someFunction(item, x1, x2)
    yield item

可以这样使用:

b.extend(generate(a, b[-1]))
4

针对你最开始提到的问题,也就是对输入序列中的每一对进行函数映射,下面的代码可以解决这个问题,而且在Python中效率也算不错。

from itertools import tee

a = range(10)
a1, a2 = tee(a)
a2.next()
b = map(someFunction, a1, a2)

至于扩展的问题,也就是你需要访问上一次迭代的结果,这种内部状态在函数式编程中叫做“展开”。不过,Python并没有提供这种展开的结构,主要是因为在这种情况下,使用for循环会更容易理解,而且通常也会更快。为了让代码更符合Python的风格,我建议把成对迭代的部分提取到一个函数中,并创建一个明确的循环变量。

def pairwise(seq):
    a, b = tee(seq)
    b.next()
    return izip(a, b)

def unfold_over_pairwise(unfolder, seq, initial):
    state = initial
    for cur_item, next_item in pairwise(seq):
        state = unfolder(state, cur_item, next_item)
        yield state

b = [something]
b.extend(unfold_over_pairwise(someFunction, a, initial=b[-1]))

如果循环的开销真的成了问题,那说明someFunction这个函数应该非常简单。在这种情况下,最好是用更快的语言,比如C,来写整个循环。

8

考虑一下这个

def make_b( a, seed ):
    yield seed
    for a,b in zip( a[:-1], a[1:] ):
        seed= someFunction( seed, a, b )
        yield seed

这让你可以做到这个

a = xrange(10)
b= list(make_b(a,something))

注意,你通常可以使用这个:

b = make_b(a)

而不是实际创建一个 b 列表。将 b 作为生成器函数可以节省大量存储空间(还有一些时间),因为你可能根本不需要一个 list 对象。通常,你只需要一些可以迭代的东西。

对于 a 也是一样。它不一定要是一个 list,只要是一些可以迭代的东西——比如带有 yield 语句的生成器函数。

撰写回答