我有一个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的方法。 该列表在迭代后被丢弃,因此不需要保留。 也许这样的事情会更好?
while ints:
foo += ints[0] * ints[1] + ints[2] * ints[3]
ints[0:4] = []
不过,还是不太“正确”。 :-/
相关问题: 如何在Python中将列表分成均匀大小的块?
#1楼
此问题的理想解决方案适用于迭代器(而不仅仅是序列)。 它也应该很快。
这是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美元。
但是,这对我来说真的不起作用,因为结果被填充为甚至大小的组。 没有填充的解决方案稍微复杂一些。 最幼稚的解决方案可能是:
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
#2楼
另一个答案,其优点是:
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)
打包,或者设置一个标志并耗尽迭代器。
#3楼
与其他建议类似,但不完全相同,我喜欢这样做,因为它简单易读:
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
。
#4楼
使用小功能和事情确实对我没有吸引力。 我更喜欢只使用切片:
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:
...
#5楼
我需要一个可以与集合和生成器一起使用的解决方案。 我无法提出任何简短而又漂亮的内容,但至少可以理解。
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]]
来源:oschina
链接:https://my.oschina.net/u/3797416/blog/3161514