【发布时间】:2014-11-10 21:14:46
【问题描述】:
我有一个 Python 函数,它接受一个列表并返回一个生成器,生成每个相邻对的 2 元组,例如
>>> list(pairs([1, 2, 3, 4]))
[(1, 2), (2, 3), (3, 4)]
我考虑过使用 2 个切片的实现:
def pairs(xs):
for p in zip(xs[:-1], xs[1:]):
yield p
还有一个以更程序化的风格编写的:
def pairs(xs):
last = object()
dummy = last
for x in xs:
if last is not dummy:
yield last,x
last = x
使用range(2 ** 15) 作为输入进行测试会产生以下时间(您可以找到我的测试代码并输出here):
2 slices: 100 loops, best of 3: 4.23 msec per loop
0 slices: 100 loops, best of 3: 5.68 msec per loop
无切片实现的部分性能损失是循环中的比较 (if last is not dummy)。删除它(使输出不正确)提高了它的性能,但它仍然比 zip-a-pair-of-slices 实现慢:
2 slices: 100 loops, best of 3: 4.48 msec per loop
0 slices: 100 loops, best of 3: 5.2 msec per loop
所以,我被难住了。为什么将 2 个切片压缩在一起,有效地并行迭代列表两次,比迭代一次更快,随时更新 last 和 x?
编辑
Dan Lenski proposed第三个实现:
def pairs(xs):
for ii in range(1,len(xs)):
yield xs[ii-1], xs[ii]
这是它与其他实现的比较:
2 slices: 100 loops, best of 3: 4.37 msec per loop
0 slices: 100 loops, best of 3: 5.61 msec per loop
Lenski's: 100 loops, best of 3: 6.43 msec per loop
它甚至更慢!这让我感到莫名其妙。
编辑 2:
ssm suggested 使用itertools.izip 代替zip,甚至比zip 还要快:
2 slices, izip: 100 loops, best of 3: 3.68 msec per loop
所以,izip 是迄今为止的赢家!但仍然出于难以检查的原因。
【问题讨论】:
-
如果您
import dis并使用dis.dis(pairs)进行功能和比较,它可能会有所帮助。 -
我认为
zip实际上在开始执行产量之前在内存中创建了整个列表。itertools.izip的性能怎么样?那更接近你的一段程序代码 -
xs列表的 Python 级索引可能会减慢速度;涉及到一些范围检查。除此之外,我不确定为什么我的版本会更慢...... drat!
标签: python performance list slice