代码之家  ›  专栏  ›  技术社区  ›  Jason Christa

在python中索引和切片生成器

  •  28
  • Jason Christa  · 技术社区  · 14 年前

    假设我有一个像这样的生成器函数:

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

    理想情况下,我可以用 FIB())〔10〕 Fib([]):2 12:2] 要获取索引和切片,但目前我必须使用ITertools来完成这些工作。我不能用生成器来代替列表。

    我相信解决办法是 () 在课堂上:

    class Indexable(object):
        ....
    
    fib_seq = Indexable(fib())
    

    应该怎样 可转位的 你想让这个工作吗?

    4 回复  |  直到 9 年前
        1
  •  33
  •   unutbu    9 年前
    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] .

    编辑: 如果你愿意 它〔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 并使用这些结果 如果可能的话。否则,它会计算更多的结果,直到有足够多的结果为止。 返回索引元素或切片。

        2
  •  0
  •   Jason Christa    14 年前

    因此,根据~unutbu的代码,添加一点itertools.tee:

    import itertools
    
    class Indexable(object):
        def __init__(self, it):
            self.it = it
    
        def __iter__(self):
            self.it, cpy = itertools.tee(self.it)
            return cpy
    
        def __getitem__(self, index):
            self.it, cpy = itertools.tee(self.it)
            if type(index) is slice:
                return list(itertools.islice(cpy, index.start, index.stop, index.step))
            else:
                return next(itertools.islice(cpy, index, index+1))
    
        3
  •  0
  •   Wolph    14 年前

    如果它是一个1-use切片,那么您可以简单地使用~unutbu编写的方法。如果需要多次切片,则必须存储所有中间值,以便可以“倒带”迭代器。因为迭代器可以迭代任何东西,所以默认情况下它没有rewind方法。

    此外,由于重绕迭代器必须存储每个中间结果(在大多数情况下),因此它与简单的重绕迭代器相比没有任何好处。 list(iterator)

    基本上…您要么不需要迭代器,要么对这种情况不够具体。

        4
  •  0
  •   John La Rooy    14 年前

    以下是修改为子类列表的~unutbu答案。明显的虐待,比如 append , insert 等会产生奇怪的结果!

    你得到 __str__ __repr__ 免费的方法

    import itertools
    class Indexable(list):
        def __init__(self,it):
            self.it=it
        def __iter__(self):
            for elt in self.it:
                yield elt
        def __getitem__(self,index):
            try:
                max_idx=index.stop
            except AttributeError:
                max_idx=index
            while max_idx>=len(self):
                self.append(next(self.it))
            return list.__getitem__(self,index)