Augmented generators?
Bengt Richter
bokr at oz.net
Tue Jan 10 22:13:02 EST 2006
Sorry to reply this way. I saw this on google, but neither this nor my previous post
has shown up yet on my news server. Wonder what all this delay is lately ;-/
>From: Paul Rubin <http://phr...@NOSPAM.invalid>
>Subject: Re: Augmented generators?
>Date: 10 Jan 2006 11:03:39 -0800
>Message-ID: <7xwth7nbw4.fsf at ruckus.brouhaha.com>
>"Andrew Koenig" <a... at acm.org> writes:
>> Can anyone think of an easy technique for creating an object that acts like
>> a generator but has additional methods?
>> For example, it might be nice to be able to iterate through an associative
>> container without having to index it for each element.
>Normally you'd define a class and give it __iter__ and next operations.
>I guess that can get messy. Does it qualify as easy for your purposes?
>> Of course I can write such a beast as a class, but that prevents me from
>> taking advantage of the yield statement in its implementation.
>You can make an internal function that's a generator with a yield
>statement (or a generator expression instead of a function, if simple
>enough). The class's 'next' method would loop through the generator
>and return each value from it.
>Let me try your example:
This suffers from the same problem as my first go (an exhausted iterator
is not supposed to restart).
>>> class kviter:
... def __init__(self, d):
... self.d = d
... def __iter__(self):
... def iter1(d, s=self):
... for k in d:
... # lambda not really needed here, but you wanted value to
... # be callable instead of just an attribute
... s.value = (lambda r=d[k]: r)
... yield k
... return iter1(self.d)
...
Using my example with your iterator:
>>> it = kviter(dict(enumerate('abcd')))
>>> for i in it:
... if i%2: print i, it.value()
...
1 b
3 d
Ok, but this shouldn't happen at this point:
>>> for i in it:
... if i%2: print i, it.value()
...
1 b
3 d
Whereas,
>>> class augiter(object):
... def __init__(self, d):
... self._it = self._gen(d)
... def __iter__(self): return self
... def _gen(self, d):
... for k, self._v in d.items(): yield k
... def next(self): return self._it.next()
... def value(self): return self._v
...
>>> it = augiter(dict(enumerate('abcd')))
>>> for i in it:
... if i%2: print i, it.value()
...
1 b
3 d
>>> for i in it:
... if i%2: print i, it.value()
...
>>>
Letting __init__ create the generator instance by calling a bound
method coded with yield makes integrating the latter style pretty easy,
even though you still need __iter__ and next methods.
Hm, you could factor that out into a base class though:
(then you just need the convention that a derived class must define at least
the _gen method. Then augment to taste)
>>> class AugiterBase(object):
... def __init__(self, *args, **kw):
... self._it = self._gen(*args, **kw)
... def __iter__(self): return self
... def next(self): return self._it.next()
...
>>> class DervAugit(AugiterBase):
... def _gen(self, d):
... for k, self._v in d.items(): yield k
... def value(self): return self._v
...
>>> it = DervAugit(dict(enumerate('abcd')))
>>> for i in it:
... if i%2: print i, it.value()
...
1 b
3 d
>>> for i in it:
... print i, it.value()
...
>>> it = DervAugit(dict(enumerate('abcd')))
>>> for i in it:
... print i, it.value()
...
0 a
1 b
2 c
3 d
Regards,
Bengt Richter
More information about the Python-list
mailing list