How about adding slice notation to iterators/generators?

Gabriel Genellina gagsl-py2 at yahoo.com.ar
Sun Oct 18 19:43:00 EDT 2009


En Sun, 18 Oct 2009 19:53:20 -0200, Eloff <dan.eloff at gmail.com> escribió:

> On Oct 16, 7:38 pm, Carl Banks <pavlovevide... at gmail.com> wrote:
>
>> You would burden everyone who writes a custom iterator to provide a
>> __getitem__ method just because you're too lazy to type out the word
>> islice?
>
> No, of course not. That would be stupid. Custom iterators are
> iterators, so they would also get the default __getitem__ which would
> work just perfectly for them. If they needed to override it, they
> could. Remember, my proposal was to add a default __getitem__ for
> iterators that is really just islice.

Note that iterators don't have a common base class - so where would such  
__getitem__ reside? *Anything* with a next/__next__ method is an iterator.
In some, very limited cases, you can 'inject' a __getitem__ method into an  
existing iterator:

py> from itertools import islice
py>
py> def __getitem__(self, subscript):
...   if isinstance(subscript, slice):
...     return islice(self, subscript.start, subscript.stop,  
subscript.step)
...   elif isinstance(subscript, int):
...     return next(islice(self, subscript, subscript+1, 1))
...
py> def add_slice_support(iterator):
...   oldtype = type(iterator)
...   newtype = type(oldtype.__name__, (oldtype,), {'__getitem__':  
__getitem__})
...   iterator.__class__ = newtype
...   return iterator

Unfortunately this only works for user-defined iterators:

py> add_slice_support(range(30))
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File "<stdin>", line 4, in add_slice_support
TypeError: __class__ assignment: only for heap types

And even then, there are surprises:

py> class Foo(object):
...   i = 0
...   def __iter__(self): return self
...   def next(self):
...     ret, self.i = self.i, self.i+1
...     return ret
...
py> it = add_slice_support(Foo())
py> print it[2]
2
py> for z in it[5:10]: print z
...
8   # 8???
9
10
11
12
py> for z in it[5:10]: print z
...
18  # 18???
19
20
21
22
py>

> The main use case I'm thinking about here is skipping elements or
> limiting results from iterators, usually in for loops:
>
> for x in my_iter[5:]:
>     ...
>
> for x in my_iter[:5]:
>     ...
>
> Clearly that's easier and more readable than using islice. Nobody has
> yet to provide a concrete reason why that would be a _bad thing_. That
> doesn't make it a _good thing_, either.

You'll have to remember to never reuse my_iter again, as my example above  
shows. Or keep track of the past items so you can adjust the indices. But  
anyway you can't retrieve those past items, unless you maintain them all  
in a list and take the memory penalty. Or just use islice when needed -  
only six letters, and you avoid all those problems... ;)

-- 
Gabriel Genellina




More information about the Python-list mailing list