我有一个Python脚本,它将一个整数列表做为输入,我须要一次处理四个整数。 不幸的是,我没法控制输入,或者将其做为四元素元组的列表传递。 目前,我正在以这种方式对其进行迭代: python
for i in xrange(0, len(ints), 4): # dummy op for example code foo += ints[i] * ints[i + 1] + ints[i + 2] * ints[i + 3]
不过,它看起来很像“ C思惟”,这使我怀疑还有一种处理这种状况的更Python的方法。 该列表在迭代后被丢弃,所以不须要保留。 也许这样的事情会更好? app
while ints: foo += ints[0] * ints[1] + ints[2] * ints[3] ints[0:4] = []
不过,仍是不太“正确”。 :-/ 测试
相关问题: 如何在Python中将列表分红均匀大小的块? spa
此问题的理想解决方案适用于迭代器(而不单单是序列)。 它也应该很快。 code
这是itertools文档提供的解决方案: 对象
def grouper(n, iterable, fillvalue=None): #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" args = [iter(iterable)] * n return itertools.izip_longest(fillvalue=fillvalue, *args)
在Mac book air上使用ipython的%timeit
,每一个循环可得到47.5美圆。 ip
可是,这对我来讲真的不起做用,由于结果被填充为甚至大小的组。 没有填充的解决方案稍微复杂一些。 最幼稚的解决方案多是: 内存
def grouper(size, iterable): i = iter(iterable) while True: out = [] try: for _ in range(size): out.append(i.next()) except StopIteration: yield out break yield out
简单但很慢:每一个循环693 us 文档
我能想到的最佳解决方案是将islice
用于内部循环: get
def grouper(size, iterable): it = iter(iterable) while True: group = tuple(itertools.islice(it, None, size)) if not group: break yield group
使用相同的数据集,每一个循环可得到305 us。
没法以比这更快的速度得到纯解决方案,我为如下解决方案提供了一个重要的警告:若是输入数据中包含filldata
实例,则可能会获得错误的答案。
def grouper(n, iterable, fillvalue=None): #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" args = [iter(iterable)] * n for i in itertools.izip_longest(fillvalue=fillvalue, *args): if tuple(i)[-1] == fillvalue: yield tuple(v for v in i if v != fillvalue) else: yield i
我真的不喜欢这个答案,可是速度更快。 每一个循环124 us
另外一个答案,其优势是:
1)容易理解
2)适用于任何可迭代的对象,而不单单是序列(上面的某些答案会在文件句柄上阻塞)
3)不会一次将块所有加载到内存中
4)不在内存中对同一迭代器创建一个大块的引用列表
5)列表末尾不填充填充值
话虽如此,我尚未计时,因此它可能比一些更聪明的方法要慢,而且某些优势可能与用例无关。
def chunkiter(iterable, size): def inneriter(first, iterator, size): yield first for _ in xrange(size - 1): yield iterator.next() it = iter(iterable) while True: yield inneriter(it.next(), it, size) In [2]: i = chunkiter('abcdefgh', 3) In [3]: for ii in i: for c in ii: print c, print '' ...: a b c d e f g h
更新:
因为内循环和外循环从同一个迭代器中提取值,所以带来了一些缺点:
1)Continue在外部循环中没法按预期工做-它只是继续到下一个项目,而不是跳过一个块。 可是,这彷佛不是问题,由于在外循环中没有要测试的东西。
2)break在内部循环中没法正常工做-控件将在迭代器中的下一个项目中再次进入内部循环。 要跳过整个块,能够将内部迭代器(上面的ii)包装在一个元组中,例如for c in tuple(ii)
中将for c in tuple(ii)
打包,或者设置一个标志并耗尽迭代器。
与其余建议相似,但不彻底相同,我喜欢这样作,由于它简单易读:
it = iter([1, 2, 3, 4, 5, 6, 7, 8, 9]) for chunk in zip(it, it, it, it): print chunk >>> (1, 2, 3, 4) >>> (5, 6, 7, 8)
这样,您将不会获得最后的部分块。 若是要获取(9, None, None, None)
做为最后一块,只需使用itertools
izip_longest
。
使用小功能和事情确实对我没有吸引力。 我更喜欢只使用切片:
data = [...] chunk_size = 10000 # or whatever chunks = [data[i:i+chunk_size] for i in xrange(0,len(data),chunk_size)] for chunk in chunks: ...
我须要一个能够与集合和生成器一块儿使用的解决方案。 我没法提出任何简短而又漂亮的内容,但至少能够理解。
def chunker(seq, size): res = [] for el in seq: res.append(el) if len(res) == size: yield res res = [] if res: yield res
清单:
>>> list(chunker([i for i in range(10)], 3)) [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
组:
>>> list(chunker(set([i for i in range(10)]), 3)) [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
发电机:
>>> list(chunker((i for i in range(10)), 3)) [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]