通过 itertools 与直接 python 分块会给你带来额外的好处吗?
Does chunking via itertools vs straight python buy you anything extra?
我发现 two different ways 将 iterable
拆分为 "chunks"(超过 1 项)。
One method uses itertools
:
from itertools import izip_longest
def grouper(iterable, n, fillvalue=None):
args = [iter(iterable)] * n
return izip_longest(*args, fillvalue=fillvalue)
the other method is straight python:
def chunker(seq, size):
return (seq[pos:pos + size] for pos in xrange(0, len(seq), size))
itertools 的实现能给你带来什么吗"extra"?
"extra" 可能会更快、更灵活或更安全。
我问是因为这里显示的 itertools 实现绝对不是 readable/intuitive IMO。
grouper
可以与 any iterable 一起使用——包括生成器和无限迭代器。
chunker
只能与 sequences 一起使用——其长度可预先知道的迭代器。
from itertools import izip_longest
def grouper(iterable, n, fillvalue=None):
args = [iter(iterable)] * n
return izip_longest(*args, fillvalue=fillvalue)
def chunker(seq, size):
return (seq[pos:pos + size] for pos in xrange(0, len(seq), size))
x = (i**2 for i in range(5))
print(list(grouper(x, 3)))
# [(0, 1, 4), (9, 16, None)]
print(list(chunker(x, 3)))
# TypeError: object of type 'generator' has no len()
这两个功能并不等同。有几点不同:
您的 grouper
适用于任何可迭代对象,包括迭代器和生成器,而 chunker
需要支持索引的序列([...]
)。
>>> it = lambda : (i for i in range(6)) # creates a generator when called
>>> list(grouper(it(), 3))
[(0, 1, 2), (3, 4, 5)]
>>> list(chunker(it(), 3))
TypeError: object of type 'generator' has no len()
请注意,其他答案已经提到了这一点!
如果块大小不是长度的约数,chunker
最后一个元素将小于块大小。 OTOH grouper
将用一些填充值填充它。此外 chunker
将 return 与原始类型相同,而 grouper
将 return tuple
s:
>>> list(grouper([1,2,3,4,5], 3))
[(1, 2, 3), (4, 5, None)]
>>> list(chunker([1,2,3,4,5], 3))
[[1, 2, 3], [4, 5]]
grouper
使用高性能内置函数 iter
和 zip_longest
。这些将非常快。以牺牲可读性为代价。然而,这可以使它比 chunker
:
快得多
l = list(range(10000))
%timeit list(grouper(l, 10))
# 320 µs ± 6.39 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit list(chunker(l, 10))
# 1.22 ms ± 19 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
所以 grouper
是一种比 chunker
更快更通用的方法。但是,根据具体情况,使用 chunker
可能更有用,例如,如果您不喜欢 "fill" 部分或想要保留 "chunks".[= 的类型28=]
我发现 two different ways 将 iterable
拆分为 "chunks"(超过 1 项)。
One method uses itertools
:
from itertools import izip_longest
def grouper(iterable, n, fillvalue=None):
args = [iter(iterable)] * n
return izip_longest(*args, fillvalue=fillvalue)
the other method is straight python:
def chunker(seq, size):
return (seq[pos:pos + size] for pos in xrange(0, len(seq), size))
itertools 的实现能给你带来什么吗"extra"?
"extra" 可能会更快、更灵活或更安全。
我问是因为这里显示的 itertools 实现绝对不是 readable/intuitive IMO。
grouper
可以与 any iterable 一起使用——包括生成器和无限迭代器。
chunker
只能与 sequences 一起使用——其长度可预先知道的迭代器。
from itertools import izip_longest
def grouper(iterable, n, fillvalue=None):
args = [iter(iterable)] * n
return izip_longest(*args, fillvalue=fillvalue)
def chunker(seq, size):
return (seq[pos:pos + size] for pos in xrange(0, len(seq), size))
x = (i**2 for i in range(5))
print(list(grouper(x, 3)))
# [(0, 1, 4), (9, 16, None)]
print(list(chunker(x, 3)))
# TypeError: object of type 'generator' has no len()
这两个功能并不等同。有几点不同:
您的
grouper
适用于任何可迭代对象,包括迭代器和生成器,而chunker
需要支持索引的序列([...]
)。>>> it = lambda : (i for i in range(6)) # creates a generator when called >>> list(grouper(it(), 3)) [(0, 1, 2), (3, 4, 5)] >>> list(chunker(it(), 3)) TypeError: object of type 'generator' has no len()
请注意,其他答案已经提到了这一点!
如果块大小不是长度的约数,
chunker
最后一个元素将小于块大小。 OTOHgrouper
将用一些填充值填充它。此外chunker
将 return 与原始类型相同,而grouper
将 returntuple
s:>>> list(grouper([1,2,3,4,5], 3)) [(1, 2, 3), (4, 5, None)] >>> list(chunker([1,2,3,4,5], 3)) [[1, 2, 3], [4, 5]]
快得多grouper
使用高性能内置函数iter
和zip_longest
。这些将非常快。以牺牲可读性为代价。然而,这可以使它比chunker
:l = list(range(10000)) %timeit list(grouper(l, 10)) # 320 µs ± 6.39 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) %timeit list(chunker(l, 10)) # 1.22 ms ± 19 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
所以 grouper
是一种比 chunker
更快更通用的方法。但是,根据具体情况,使用 chunker
可能更有用,例如,如果您不喜欢 "fill" 部分或想要保留 "chunks".[= 的类型28=]