我有一个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-think",这让我怀疑有更多的pythonic方式来处理这种情况.迭代后会丢弃该列表,因此无需保留.也许这样的事情会更好吗?
while ints: foo += ints[0] * ints[1] + ints[2] * ints[3] ints[0:4] = []
但是,仍然没有"感觉"正确.: - /
相关问题:如何在Python中将列表拆分为大小均匀的块?
def chunker(seq, size): return (seq[pos:pos + size] for pos in range(0, len(seq), size)) # (in python 2 use xrange() instead of range() to avoid allocating a list)
简单.简单.快速.适用于任何序列:
text = "I am a very, very helpful text" for group in chunker(text, 7): print repr(group), # 'I am a ' 'very, v' 'ery hel' 'pful te' 'xt' print '|'.join(chunker(text, 10)) # I am a ver|y, very he|lpful text animals = ['cat', 'dog', 'rabbit', 'duck', 'bird', 'cow', 'gnu', 'fish'] for group in chunker(animals, 3): print group # ['cat', 'dog', 'rabbit'] # ['duck', 'bird', 'cow'] # ['gnu', 'fish']
从Python的itertools文档的recipes部分修改:
from itertools import zip_longest def grouper(iterable, n, fillvalue=None): args = [iter(iterable)] * n return zip_longest(*args, fillvalue=fillvalue)
示例
在伪代码中保持示例简洁.
grouper('ABCDEFG', 3, 'x') --> 'ABC' 'DEF' 'Gxx'
注意: izip_longest
Python 2.6的新功能.在Python 3中使用zip_longest
.
我是粉丝
chunk_size= 4 for i in range(0, len(ints), chunk_size): chunk = ints[i:i+chunk_size] # process chunk of size <= chunk_size
import itertools def chunks(iterable,size): it = iter(iterable) chunk = tuple(itertools.islice(it,size)) while chunk: yield chunk chunk = tuple(itertools.islice(it,size)) # though this will throw ValueError if the length of ints # isn't a multiple of four: for x1,x2,x3,x4 in chunks(ints,4): foo += x1 + x2 + x3 + x4 for chunk in chunks(ints,4): foo += sum(chunk)
其他方式:
import itertools def chunks2(iterable,size,filler=None): it = itertools.chain(iterable,itertools.repeat(filler,size-1)) chunk = tuple(itertools.islice(it,size)) while len(chunk) == size: yield chunk chunk = tuple(itertools.islice(it,size)) # x2, x3 and x4 could get the value 0 if the length is not # a multiple of 4. for x1,x2,x3,x4 in chunks2(ints,4,0): foo += x1 + x2 + x3 + x4
from itertools import izip_longest def chunker(iterable, chunksize, filler): return izip_longest(*[iter(iterable)]*chunksize, fillvalue=filler)
我需要一个也适用于集合和生成器的解决方案.我无法想出任何非常简短的东西,但它至少是可读的.
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]]
这个问题的理想解决方案适用于迭代器(不仅仅是序列).它也应该很快.
这是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)
%timeit
在我的mac书籍空气中使用ipython ,我得到每循环47.5 us.
但是,这对我来说真的不起作用,因为结果被填充为偶数组.没有填充的解决方案稍微复杂一些.最天真的解决方案可能是:
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
用于内循环:
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
与其他提案类似,但不完全相同,我喜欢这样做,因为它简单易读:
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)
的最后一个块,只需使用izip_longest
从itertools
.
由于没有人提到它,所以这里有一个zip()
解决方案:
>>> def chunker(iterable, chunksize): ... return zip(*[iter(iterable)]*chunksize)
仅当序列的长度始终可被块大小整除,或者如果您不关心尾随的块时,它才起作用。
例:
>>> s = '1234567890' >>> chunker(s, 3) [('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9')] >>> chunker(s, 4) [('1', '2', '3', '4'), ('5', '6', '7', '8')] >>> chunker(s, 5) [('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]
或使用itertools.izip返回迭代器而不是列表:
>>> from itertools import izip >>> def chunker(iterable, chunksize): ... return izip(*[iter(iterable)]*chunksize)
可以使用@ ?????????的答案来固定填充:
>>> from itertools import chain, izip, repeat >>> def chunker(iterable, chunksize, fillvalue=None): ... it = chain(iterable, repeat(fillvalue, chunksize-1)) ... args = [it] * chunksize ... return izip(*args)
使用map()而不是zip()修复了JF Sebastian的答案中的填充问题:
>>> def chunker(iterable, chunksize): ... return map(None,*[iter(iterable)]*chunksize)
例:
>>> s = '1234567890' >>> chunker(s, 3) [('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9'), ('0', None, None)] >>> chunker(s, 4) [('1', '2', '3', '4'), ('5', '6', '7', '8'), ('9', '0', None, None)] >>> chunker(s, 5) [('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]