很多时候,我需要按对处理一个列表。我在想什么是Pythonic和高效的方法来实现它,并在谷歌上找到了这个:
pairs = zip(t[::2], t[1::2])
我认为这已经足够符合Pythonic的标准了,但最近一次关于成语和效率的讨论(idioms versus efficiency)后,我决定进行一些测试:
import time
from itertools import islice, izip
def pairs_1(t):
return zip(t[::2], t[1::2])
def pairs_2(t):
return izip(t[::2], t[1::2])
def pairs_3(t):
return izip(islice(t,None,None,2), islice(t,1,None,2))
A = range(10000)
B = xrange(len(A))
def pairs_4(t):
# ignore value of t!
t = B
return izip(islice(t,None,None,2), islice(t,1,None,2))
for f in pairs_1, pairs_2, pairs_3, pairs_4:
# time the pairing
s = time.time()
for i in range(1000):
p = f(A)
t1 = time.time() - s
# time using the pairs
s = time.time()
for i in range(1000):
p = f(A)
for a, b in p:
pass
t2 = time.time() - s
print t1, t2, t2-t1
以下是我的电脑上的结果:
1.48668909073 2.63187503815 1.14518594742
0.105381965637 1.35109519958 1.24571323395
0.00257992744446 1.46182489395 1.45924496651
0.00251388549805 1.70076990128 1.69825601578
如果我正确理解的话,这应该意味着Python中列表、列表索引和列表切片的实现非常高效。这是一个令人欣慰且出乎意料的结果。
有没有另一种“更好”的方法来成对遍历列表?
请注意,如果列表元素数量为奇数,则最后一个元素将不在任何一对中。
哪种方法才是确保包含所有元素的正确方法?
我从答案中添加了这两个建议。
def pairwise(t):
it = iter(t)
return izip(it, it)
def chunkwise(t, size=2):
it = iter(t)
return izip(*[it]*size)
这是结果:
0.00159502029419 1.25745987892 1.25586485863
0.00222492218018 1.23795199394 1.23572707176
迄今为止的结果
最符合Python风格且非常高效:
pairs = izip(t[::2], t[1::2])
最高效、最符合Python风格的写法:
pairs = izip(*[iter(t)]*2)
我花了一点时间才明白第一个答案使用了两个迭代器而第二个答案使用了一个。
为了应对奇数个元素的序列,建议增加一个元素(None
)来增补原始序列,该元素与前一个最后一个元素配对。这可以通过使用itertools.izip_longest()
来实现。
最后
请注意,在Python 3.x中,zip()
行为类似于itertools.izip()
,而itertools.izip()
已经不存在了。
timeit
模块。 - SilentGhostzip(t[::2], t[1::2])
会留下最后一个。 - Pydermanitertools.izip_longest()
。 - Apalala