在Python中对生成器进行索引和切片

33 投票
6 回答
17332 浏览
提问于 2025-04-15 19:39

假设我有一个生成器函数,长得像这样:

def fib():
    x,y = 1,1
    while True:
        x, y = y, x+y
        yield x

理想情况下,我可以直接用 fib()[10]fib()[2:12:2] 来获取索引和切片,但现在我必须使用 itertools 来处理这些事情。我不能用生成器直接替代列表。

我认为解决办法是把 fib() 放进一个类里:

class Indexable(object):
    ....

fib_seq = Indexable(fib())

那这个 Indexable 应该长什么样,才能让它工作呢?

6 个回答

1

如果你只用一次切片,那你可以直接用~unutbu写的方法。如果你需要多次切片,就得把所有中间的值都存起来,这样才能“倒回去”使用迭代器。因为迭代器可以遍历任何东西,所以默认情况下是没有倒回功能的。

而且,由于一个可以倒回的迭代器需要存储每一个中间结果,通常情况下,它并没有比直接用list(iterator)更有优势。

总的来说... 要么你根本不需要迭代器,要么你对情况的描述不够具体。

2

要对生成器进行切片,你可以使用来自 itertoolsislice 函数。

from itertools import islice

for i in islice(generator, 5):
    # Will be taken first 5 elems

for i in islice(generator, 5, None):
    # Will be taken everything starting at 5th
38
import itertools

class Indexable(object):
    def __init__(self,it):
        self.it = iter(it)
    def __iter__(self):
        return self.it
    def __getitem__(self,index):
        try:
            return next(itertools.islice(self.it,index,index+1))
        except TypeError:
            return list(itertools.islice(self.it,index.start,index.stop,index.step))

你可以这样使用它:

it = Indexable(fib())
print(it[10])
#144
print(it[2:12:2])
#[610, 1597, 4181, 10946, 28657]

注意,it[2:12:2] 并不会返回 [3, 8, 21, 55, 144],因为在调用 it[10] 时,迭代器已经向前移动了11个元素。

编辑:如果你希望 it[2:12:2] 返回 [3, 8, 21, 55, 144],那么你可以试试这个:

class Indexable(object):

    def __init__(self, it):
        self.it = iter(it)
        self.already_computed = []

    def __iter__(self):
        for elt in self.it:
            self.already_computed.append(elt)
            yield elt

    def __getitem__(self, index):
        try:
            max_idx = index.stop
        except AttributeError:
            max_idx = index
        n = max_idx - len(self.already_computed) + 1
        if n > 0:
            self.already_computed.extend(itertools.islice(self.it, n))
        return self.already_computed[index]

这个版本会把结果保存在 self.already_computed 中,并在可能的情况下使用这些结果。否则,它会继续计算更多的结果,直到有足够的结果可以返回指定的元素或切片。

撰写回答