basic generator question

Steven D'Aprano steve+comp.lang.python at pearwood.info
Wed Feb 4 11:09:37 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, there is no need for this "obj" class, it just adds a pointless
layer of indirection. Perhaps it was written by a Java programmer?

http://steve-yegge.blogspot.com.au/2006/03/execution-in-kingdom-of-nouns.html

Instead of:

x = obj(something_callable)
# later
result = x()

just do:

x = something_callable
result = x()

or even

result = something_callable()


 
> Now I want gen to be a callable that repeats N times.

What happens on the sixth time? The world ends? Most callables can be called
indefinitely.

I'm going to assume an exception. Later I'll use a RuntimeException, but you
can do anything you like.


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

The built-in function `next` almost does what we want, except the exception
raised is inappropriate. Unless you are very careful, you don't want to
allow StopIteration to propagate, lest it be caught in an unexpected place
and cause surprises. (Not the pleasant kind.)


py> from functools import partial
py> f = partial(next, rpt("hello", 2)())  # note the extra parens
py> f()
'hello'
py> f()
'hello'
py> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration


But I wouldn't use rpt as given. I'd say:

def repeat(obj, count):
    for i in range(count):
        yield obj
    raise RuntimeError  # or whatever

mycallable = partial(next, repeat("hello", 5))  # no extra parens

And now pass mycallable to your other class.



P.S. your naming conventions give me a blood clot. Class "rpt" taking an
argument "rpt" -- I lost six months of life just from reading that.


-- 
Steven




More information about the Python-list mailing list