basic generator question
Peter Otten
__peter__ at web.de
Wed Feb 4 11:02:24 EST 2015
Neal Becker wrote:
> I have an object that expects to call a callable to get a value:
>
> class obj:
> def __init__ (self, gen):
> self.gen = gen
> def __call__ (self):
> return self.gen()
As written that looks a bit like
if boolean_expression == True: ...
as you could replace
inst = obj(callable)
with
inst = callable
but that may be an artifact of the example.
> Now I want gen to be a callable that repeats N times. I'm thinking, this
> sounds perfect for yield
>
> class rpt:
> def __init__ (self, value, rpt):
> self.value = value; self.rpt = rpt
> def __call__ (self):
> for i in range (self.rpt):
> yield self.value
>
> so I would do:
>
> my_rpt_obj = obj (rpt ('hello', 5))
>
> to repeat 'hello' 5 times (for example).
What do you expect to happen when my_rpt_obj is called the sixth time?
> But this doesn't work. when obj calls self.gen(), that returns a
> generator, not the next value.
>
> How can I make this work? I can't change the interface of the existing
> class obj, which expects a callable to get the next value.
>>> class Obj:
... def __init__(self, gen):
... self.gen = iter(gen)
... def __call__(self):
... return next(self.gen)
...
>>> class Repeat:
... def __init__(self, value, times):
... self.value = value
... self.times = times
... def __iter__(self):
... for i in range(self.times):
... yield self.value
...
>>> r = Obj(Repeat("hello", 3))
>>> r()
'hello'
>>> r()
'hello'
>>> r()
'hello'
>>> r()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __call__
StopIteration
Instead of the Repeat class you may use a generator:
>>> def repeat(value, times):
... for i in range(times):
... yield value
...
>>> r = Obj(repeat("hello", 2))
>>> r()
'hello'
>>> r()
'hello'
>>> r()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __call__
StopIteration
This is for demonstration purposes as there is already itertools.repeat():
>>> import itertools
>>> r = Obj(itertools.repeat("world", 2))
>>> r()
'world'
>>> r()
'world'
>>> r()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __call__
StopIteration
Depending on your actual need you may also omit the Obj() class:
>>> import functools
>>> r = functools.partial(next, itertools.repeat("goodbye", 2))
>>> r()
'goodbye'
>>> r()
'goodbye'
>>> r()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Somewhat less formal you can bind the iterator method directly:
>>> r = itertools.repeat("GOODBYE", 2).__next__ # next in Python 2
>>> r()
'GOODBYE'
>>> r()
'GOODBYE'
>>> r()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
More information about the Python-list
mailing list