遍历列表切片

40 投票
10 回答
44675 浏览
提问于 2025-04-15 13:53

我想要一个算法,可以对列表的切片进行遍历。切片的大小是在函数外部设置的,可能会有所不同。

我脑海中的想法是这样的:

for list_of_x_items in fatherList:
    foo(list_of_x_items)

有没有办法正确地定义 list_of_x_items,或者用其他方式在 Python 2.5 中实现这个功能呢?


编辑1:澄清 “分区”和“滑动窗口”这两个词听起来都适合我的任务,但我并不是专家。所以我会更深入地解释一下问题,并补充到问题中:

fatherList 是一个多层的 numpy.array,我是从一个文件中获取的。这个函数需要找到一系列的平均值(用户提供系列的长度)。我使用 mean() 函数来计算平均值。现在为了扩展问题:

编辑2:你提供的函数怎么修改,才能存储额外的项,并在下一个 fatherList 输入到函数时使用它们呢?

举个例子,如果列表的长度是 10,而每个块的大小是 3,那么列表的第 10 个成员会被存储,并在下一个列表的开头添加上。


相关链接:

10 个回答

22

如果你想要处理任何可以遍历的东西,可以使用这些函数:

from itertools import chain, islice

def ichunked(seq, chunksize):
    """Yields items from an iterator in iterable chunks."""
    it = iter(seq)
    while True:
        yield chain([it.next()], islice(it, chunksize-1))

def chunked(seq, chunksize):
    """Yields items from an iterator in list chunks."""
    for chunk in ichunked(seq, chunksize):
        yield list(chunk)
74

如果你想把一个列表分成几块,可以用这个小技巧:

list_of_slices = zip(*(iter(the_list),) * slice_size)

举个例子:

>>> zip(*(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]

如果列表里的项目数量不能被切片大小整除,而你想用 None 来填充列表,可以这样做:

>>> map(None, *(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]

这其实是个小窍门。


好吧,我来解释一下这个技巧是怎么工作的。虽然有点复杂,但我会尽量讲清楚。

首先,先了解一些背景知识:

在 Python 中,你可以用一个数字来乘以一个列表,像这样:

[1, 2, 3] * 3 -> [1, 2, 3, 1, 2, 3, 1, 2, 3]
([1, 2, 3],) * 3 -> ([1, 2, 3], [1, 2, 3], [1, 2, 3])

还有一个 迭代器 对象只能被消费一次,像这样:

>>> l=iter([1, 2, 3])
>>> l.next()
1
>>> l.next()
2
>>> l.next()
3

zip 函数会返回一个元组的列表,其中第 i 个元组包含每个参数序列或可迭代对象的第 i 个元素。例如:

zip([1, 2, 3], [20, 30, 40]) -> [(1, 20), (2, 30), (3, 40)]
zip(*[(1, 20), (2, 30), (3, 40)]) -> [[1, 2, 3], [20, 30, 40]]

在 zip 前面的 * 是用来解包参数的。你可以在 这里 找到更多细节。

所以

zip(*[(1, 20), (2, 30), (3, 40)])

实际上等同于

zip((1, 20), (2, 30), (3, 40))

但可以处理可变数量的参数。

现在回到这个小技巧:

list_of_slices = zip(*(iter(the_list),) * slice_size)

iter(the_list) -> 把列表转换成一个迭代器。

(iter(the_list),) * N -> 这会生成 N 个对 the_list 迭代器的引用。

zip(*(iter(the_list),) * N) -> 这会把这些迭代器的列表传给 zip。然后 zip 会把它们分组为 N 大小的元组。但因为这 N 个项目实际上都是对同一个迭代器 iter(the_list) 的引用,所以结果会是对原始迭代器的 next() 的重复调用。

希望这样解释清楚了。我建议你使用更容易理解的解决方案。我之所以提到这个技巧,是因为我觉得它很有意思。

8

对问题最后部分的回答:

问题更新:如何修改你提供的函数,以便存储额外的项目,并在下一个父列表传入函数时使用它们?

如果你需要保存状态,可以用一个对象来实现。

class Chunker(object):
    """Split `iterable` on evenly sized chunks.

    Leftovers are remembered and yielded at the next call.
    """
    def __init__(self, chunksize):
        assert chunksize > 0
        self.chunksize = chunksize        
        self.chunk = []

    def __call__(self, iterable):
        """Yield items from `iterable` `self.chunksize` at the time."""
        assert len(self.chunk) < self.chunksize
        for item in iterable:
            self.chunk.append(item)
            if len(self.chunk) == self.chunksize:
                # yield collected full chunk
                yield self.chunk
                self.chunk = [] 

例子:

chunker = Chunker(3)
for s in "abcd", "efgh":
    for chunk in chunker(s):
        print ''.join(chunk)

if chunker.chunk: # is there anything left?
    print ''.join(chunker.chunk)

输出:

abc
def
gh

撰写回答