def limit_cheat(*iterables, fillvalue=None):
return islice(zip_longest(*iterables, fillvalue=fillvalue), cheat_length)
def Kelly_Bundy_chain(first, *rest, fillvalue=None):
return zip(first, *map(chain, rest, repeat(repeat(fillvalue))))
def Kelly_Bundy_compress(first, *rest, fillvalue=None):
a, b = tee(first)
return compress(zip_longest(b, *rest, fillvalue=fillvalue), zip(a))
def CrazyChucky(*iterables, fillvalue=None):
SENTINEL = object()
for first, *others in zip_longest(*iterables, fillvalue=SENTINEL):
if first is SENTINEL:
return
others = [i if i is not SENTINEL else fillvalue for i in others]
yield (first, *others)
def Sven_Marnach(first, *rest, fillvalue=None):
rest = [iter(r) for r in rest]
for x in first:
yield x, *(next(r, fillvalue) for r in rest)
def Mad_Physicist(*args, fillvalue=None):
if not args:
return
iterators = [iter(it) for it in args]
while True:
values = []
for i, it in enumerate(iterators):
try:
value = next(it)
except StopIteration:
if i == 0:
return
iterators[i] = repeat(fillvalue)
value = fillvalue
values.append(value)
yield tuple(values)
def Kelly_Bundy_3(first, *rest, fillvalue=None):
a, b = tee(first)
return map(itemgetter(1), zip(a, zip_longest(b, *rest, fillvalue=fillvalue)))
def Kelly_Bundy_4(first, *rest, fillvalue=None):
sentinel = object()
for z in zip_longest(chain(first, [sentinel]), *rest, fillvalue=fillvalue):
if z[0] is sentinel:
break
yield z
def Kelly_Bundy_5(first, *rest, fillvalue=None):
stopped = False
def stop():
nonlocal stopped
stopped = True
return
yield
for z in zip_longest(chain(first, stop()), *rest, fillvalue=fillvalue):
if stopped:
break
yield z
import timeit
from itertools import chain, repeat, zip_longest, islice, tee, compress
from operator import itemgetter
from collections import deque
funcs = [
limit_cheat,
Kelly_Bundy_chain,
Kelly_Bundy_compress,
CrazyChucky,
Sven_Marnach,
Mad_Physicist,
Kelly_Bundy_3,
Kelly_Bundy_4,
Kelly_Bundy_5,
]
def test(args_creator):
expect = list(funcs[0](*args_creator()))
for func in funcs:
result = list(func(*args_creator()))
print(result == expect, func.__name__)
tss = [[] for _ in funcs]
for _ in range(5):
print()
print(args_creator.__name__)
for func, ts in zip(funcs, tss):
t = min(timeit.repeat(lambda: deque(func(*args_creator()), 0), number=1))
ts.append(t)
print(*('%4.1f ms ' % (t * 1e3) for t in sorted(ts)[:3]), func.__name__)
def args_few_but_long_iterables():
global cheat_length
cheat_length = 50_000
first = repeat(0, 50_000)
rest = [repeat(i, 10_000 * i) for i in range(1, 10)]
return first, *rest
def args_many_but_short_iterables():
global cheat_length
cheat_length = 50
first = repeat(0, 50)
rest = [repeat(i, i % 101) for i in range(1, 10_000)]
return first, *rest
test(args_few_but_long_iterables)
funcs[1:3] = funcs[1:3][::-1]
test(args_many_but_short_iterables)
map
的额外参数,所以花了好2-3分钟才意识到你在这里做什么 :) 我唯一的建议是添加参数fillvalue=None
并将repeat(repeat(None))
更改为repeat(repeat(fillvalue))
。 - Mad Physicistfirst = repeat(0, 50_000)
和rest = [repeat(i, 10_000 * i) for i in range(1, 10)]
,你的代码需要 75 毫秒,而我的只需要 2.7 毫秒 :-D - Kelly Bundyfirst = repeat(0, 5_000)
和rest = [repeat(i, 100 * i) for i in range(1, 100)]
),结果变化不大。 - Kelly Bundymap
比列表推导更快。更重要的是,您为每个可迭代对象创建一个repeat
,而我只创建一个共享的repeat
,并将其链接到所有可迭代对象上。您的带有单个准备好的列表推导也需要大约19.5毫秒。 - Kelly Bundy