我有一个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] = []
但是,仍然没有“感觉”正确 . : - /
相关问题:How do you split a list into evenly sized chunks in Python?
30 回答
与其他提案类似,但不完全相同,我喜欢这样做,因为它简单易读:
这样你就不会获得最后的部分块 . 如果你想将
(9, None, None, None)
作为最后一个块,只需使用itertools
中的izip_longest
.使用小功能和东西真的不吸引我;我更喜欢使用切片:
这是一个没有导入支持生成器的chunker:
使用示例:
如果您不介意使用外部包,可以使用iteration_utilities.grouper来自iteration_utilties 1.它支持所有迭代(不仅仅是序列):
打印:
如果长度不是groupsize的倍数,它还支持填充(不完整的最后一组)或截断(丢弃不完整的最后一组)最后一个:
1免责声明:我是该套餐的作者 .
您可以使用funcy库中的partition或chunks函数:
这些函数还有迭代器版本
ipartition
和ichunks
,在这种情况下效率更高 .你也可以偷看their implementation .
从Python的itertools docs的recipes部分修改:
Example
在伪代码中保持示例简洁 .
Note:
izip_longest
是Python 2.6的新功能 . 在Python 3中使用zip_longest
.另一种方法是使用
iter
的双参数形式:这可以很容易地适应使用填充(这类似于_387664的答案):
这些甚至可以组合用于可选填充:
简单 . 简单 . 快速 . 适用于任何序列:
我喜欢这种方法 . 它感觉简单而不神奇,支持所有可迭代类型,不需要导入 .
在你的第二种方法中,我会通过这样做前进到下一组4:
但是,我没有进行任何性能测量,因此我不知道哪一个可能更有效 .
话虽如此,我通常会选择第一种方法 . 它并不漂亮,但这通常是与外界联系的结果 .
发布此作为答案,因为我无法发表评论......
使用map()而不是zip()修复了J.F.Sebastian的答案中的填充问题:
例:
似乎没有一种漂亮的方法可以做到这一点 . Here是一个包含许多方法的页面,包括:
要避免所有转换到列表
import itertools
和:生产环境 :
我检查了
groupby
并且它没有转换为列表或使用len
所以我(想)这将延迟每个值的分辨率,直到它实际使用 . 可悲的是,没有一个可用的答案(此时)似乎提供了这种变化 .显然,如果你需要依次处理每个项目嵌套一个for循环g:
我对此的特别兴趣是需要使用生成器向gmail API提交最多1000个批量的更改:
首先,我设计它将字符串拆分为子字符串以解析包含hex的字符串 .
今天我把它变成了复杂但仍然很简单的发电机 .
参数:
明显的
iterable
是包含/生成/迭代输入数据的任何可迭代/迭代器/生成器,size
当然是你想要的块大小,更有趣
reductor
是一个可调用的,它接收生成器迭代块的内容 .我'd expect it to return sequence or string, but I don'吨要求 .
你可以传递这个参数,例如
list
,tuple
,set
,frozenset
,或任何发烧友 . 我传递了这个函数,返回字符串
(假设
iterable
包含/生成/遍历字符串):请注意,reductor可以通过引发异常来关闭生成器 .
condition
是一个可调用的,它接收reductor
返回的任何内容 .它决定批准并收益(通过返回评估为
True
的任何内容),或拒绝它并完成发电机的工作(通过返回任何其他或提高异常) .
当
iterable
中的元素数量不能被size
整除时,当it
耗尽时,reductor
将接收生成器产生的更少元素比size
.我们称这些元素为持续元素 .
我邀请两个函数作为这个参数传递:
lambda x:x
- 将产生持久元素 .lambda x: len(x)==<size>
- 持续元素将被拒绝 .使用等于大小的数字替换<size>
既然没有人's mentioned it yet here'是一个
zip()
解决方案:仅当序列的长度始终可以被块大小整除时才有效,或者如果不是,则不关心尾随块 .
例:
或者使用itertools.izip返回迭代器而不是列表:
可以使用@ΤΖΩΤΖΙΟΥ's answer修复填充:
很容易让
itertools.groupby
为您提供可迭代的迭代,而无需创建任何临时列表:不要被嵌套的lambdas推迟,外部lambda只运行一次以将
count()
生成器和常量100
放入内部lambda的范围内 .我用它来发送行块到mysql .
关于
J.F. Sebastian
here给出的解决方案:它很聪明,但有一个缺点 - 总是返回元组 . 如何获得字符串?
当然你可以写
''.join(chunker(...))
,但无论如何都会构建临时元组 .您可以通过编写自己的
zip
来摆脱临时元组,如下所示:然后
用法示例:
如果列表大小相同,您可以将它们组合成带有
zip()
的4元组列表 . 例如:这是
zip()
函数产生的内容:如果列表很大,并且您不想将它们组合成更大的列表,请使用
itertools.izip()
,它会生成迭代器而不是列表 .我需要一个也适用于集合和生成器的解决方案 . 我无法想出任何非常短而漂亮的东西,但它至少是可读的 .
列表:
组:
发电机:
我是粉丝
这个问题的理想解决方案适用于迭代器(不仅仅是序列) . 它也应该很快 .
这是itertools文档提供的解决方案:
在我的mac书空中使用ipython的
%timeit
,每循环得到47.5 us .但是,这对我来说真的不起作用,因为结果被填充为偶数组 . 没有填充的解决方案稍微复杂一些 . 最天真的解决方案可能是:
简单但很慢:每循环693 us
我能提出的最佳解决方案是使用
islice
作为内循环:使用相同的数据集,我得到每个循环305 us .
无法以更快的速度获得纯粹的解决方案,我提供了以下解决方案,并提出了一个重要的警告:如果您的输入数据中包含
filldata
的实例,则可能会得到错误的答案 .我真的不喜欢这个答案,但速度要快得多 . 每循环124 us
如果列表很大,执行此操作的最高性能方法是使用生成器:
我从不希望我的大块填充,所以这个要求是必不可少的 . 我发现能够处理任何可迭代的能力也是必需的 . 鉴于此,我决定延续接受的答案,https://stackoverflow.com/a/434411/1074659 .
如果由于需要比较和过滤填充值而不需要填充,则此方法的性能会受到轻微影响 . 但是,对于大块大小,此实用程序非常高效 .
单行,即兴解决方案迭代列表
x
大小4
的块 -使用NumPy很简单:
输出:
其他方式:
还有一个答案,其优点是:
1)容易理解
2)适用于任何可迭代的,而不仅仅是序列(上面的一些答案会阻塞文件句柄)
3)不会一次将块加载到内存中
4)不要在内存中创建一个长度相同的迭代器引用列表
5)列表末尾没有填充填充值
话虽如此,我还没有计时,所以它可能比一些更聪明的方法更慢,并且考虑到用例,一些优点可能无关紧要 .
Update:
由于内部和外部循环从同一个迭代器中提取值,因此存在一些缺点:
1)继续没有看起来像是一个问题,因为在外部循环中没有什么可以测试的 .
2)break在内循环中没有按预期工作 - 控件将在迭代器中的下一个项目中再次在内循环中结束 . 要跳过整个块,要么将内迭代器(上面的ii)包装在一个元组中,例如,
for c in tuple(ii)
,或者设置一个标志并耗尽迭代器 .