Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Index and Slice a Generator in Python

Lets say I have a generator function that looks like this:

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

Ideally, I could just use fib()[10] or fib()[2:12:2] to get indexes and slices, but currently I have to use itertools for these things. I can't use a generator for a drop in replacement for lists.

I believe the solution will be to wrap fib() in a class:

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

What should Indexable look like to make this work?

like image 461
Jason Christa Avatar asked Feb 23 '10 23:02

Jason Christa


People also ask

What is index and slice in Python?

“Indexing” means referring to an element of an iterable by its position within the iterable. “Slicing” means getting a subset of elements from an iterable based on their indices.

What is list indexing and slicing with an example in Python?

What are Indexing and Slicing? Indexing: Indexing is used to obtain individual elements. Slicing: Slicing is used to obtain a sequence of elements. Indexing and Slicing can be be done in Python Sequences types like list, string, tuple, range objects.

What is a slicing in Python?

Python slice() FunctionA slice object is used to specify how to slice a sequence. You can specify where to start the slicing, and where to end. You can also specify the step, which allows you to e.g. slice only every other item.


1 Answers

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)) 

You could use it like this:

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

Notice that it[2:12:2] does not return [3, 8, 21, 55, 144] since the iterator had already advanced 11 elements because of the call to it[10].

Edit: If you'd like it[2:12:2] to return [3, 8, 21, 55, 144] then perhaps use this instead:

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] 

This version saves the results in self.already_computed and uses those results if possible. Otherwise, it computes more results until it has sufficiently many to return the indexed element or slice.

like image 191
unutbu Avatar answered Oct 04 '22 06:10

unutbu