Python中一种迭代序列的Pythonic方式,每次迭代4个项目。

38

可能是重复问题:
什么是迭代列表的最“Pythonic”方法?

我正在读取一些PNG数据,每个像素有4个通道。我想逐个像素地迭代数据(意味着每4个元素= 1个像素,rgba)。

red_channel = 0
while red_channel < len(raw_png_data):
    green_channel, blue_channel, alpha_channel = red_channel +1, red_channel +2, red_channel +3
    # do something with my 4 channels of pixel data ... raw_png_data[red_channel] etc
    red_channel += 4

这种方式似乎不是很“正确”。有没有更Pythonic的方法来迭代一个序列,每次4个项目,并将这些4个项目解包?
5个回答

42

Python的itertools模块应该将所有配方作为标准函数...

您可以使用grouper函数:

from itertools import zip_longest
def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

然后你可以通过以下方式迭代像素

for r,g,b,a in grouper(4, raw_png_data):
  ....

或者,您可以使用

irpd = iter(raw_png_data)
for r,g,b,a in zip(irpd, irpd, irpd, irpd):  # use itertools.izip in Python 2.x
  ....

请注意,如果可迭代对象的长度不是4的倍数,则会截断最后几个字节。另一方面,grouper函数使用izip_longest,因此对于它来说,额外的字节将被填充为None。


35
vars = [1, 2, 3, 4, 5, 6, 7, 8]
for a, b, c, d in zip(*[iter(vars)]*4):
    print a, b, c, d

1
但请注意,此解决方案仅适用于变量列表长度为4的倍数的情况,以便每次迭代都可以处理4个变量。 - Eugene Nagorny
1
这个对我起作用,但正如@EugeneNagorny所说,任何使用这个的人都应该注意,如果你的列表项少于“商”的倍数,它将省略“余数”。例如,对于一个有十个值的列表和一个商为4的列表,它只会发出两组四个值,忽略最后两个。 - Christopher Bottoms

9
from itertools import izip
for r,g,b,a in izip(*[iter(data)]*4):
    ...

3
for r, g, b, t in (data[i:i+4] for i in xrange(0, len(data)/4*4, 4)):
    print r, g, b, t

并非所有序列都支持索引。data 可能是一个生成器。 - tzot

-4

可以尝试这样做:

for red, green, blue, alpha in raw_png_data:
    #do something

你可以一次性提取多个项目,而无需使用迭代器。 :)

编辑:这意味着raw_png_data需要是一个由4个值元组组成的列表。最符合Python风格的做法是将每个rgba组放入元组中,然后将其附加到raw_png_data并像我的示例一样进行迭代。


7
显然,除非raw_png_data是一个元组列表,否则这将无法运行。对吧? - Sjoerd
是的,但是当创建raw_png_data时,将它们分组成元组会是一个好主意,因为每组4个值是相关的。 - excid3
3
这个问题询问如何将原始序列分组成元组。 - augurar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接