sunny
sunny

Reputation: 3891

Trying to create generator object but getting function object that doesn't respond to generator calls

I'm trying to write an infinite generator that will repeat every positive integer n times. So for example, if I create f = inf_repeat(3), printing the output of f 10 times would result in:

1 1 1 2 2 2 3 3 3 4

I am close but not quite there. Here's what I've got:

    # courtesy of http://stackoverflow.com/questions/279561/what-is-the-python-equivalent-of-static-variables-inside-a-function
    # a generator that yields items instead of returning a list
    def static_var(varname, value):
        def decorate(func):
            setattr(func, varname, value)
            return func
        return decorate

    def inf_repeat(k):
        count_limit = k
        @static_var("counter", 0)
        @static_var("number", 0)
        def func():
            while True:
                if  func.counter == count_limit:
                    func.counter = 0
                    func.number += 1
                func.counter += 1
                yield func.number
        return func

My problem is that this doesn't behave entirely like an iterator. The following commands work:

f3 = inf_repeat(3)
print next(f3())

But it's irritating to have to call f3 with parens. I'd like to be able to use the standard iterator syntax I've seen, such as:

print(f3.next())

and

new_list = [iter(f3)]*5

What do I need to modify in my function to get to that point? Looking at a variety of generator tutorials, it seemed that yield was sufficient to create a generator, but clearly that's not the case.

Also I have no objective to using a module. I checked itertools but maybe I missed something that could do what I want without all this code?

Upvotes: 2

Views: 977

Answers (4)

acushner
acushner

Reputation: 9946

def f(n):
    i = 0
    while True:
        yield i // n
        i += 1

Upvotes: 1

Pynchia
Pynchia

Reputation: 11590

Another solution using itertools

import itertools as it

def inf_repeat(k):
    for i in it.count(1):
        for j in [i]*k:
            yield j

for n in inf_repeat(3): print n

produces

1
1
1
2
2
2
...

Upvotes: 1

Alex Hall
Alex Hall

Reputation: 36033

Here's a simple solution:

def inf_repeat(N):
    i = 1
    while True:
        for n in range(N):
            yield i
        i += 1

# Testing:
f = inf_repeat(3)
for j in range(10):
    print f.next()

Here's a solution using itertools:

def inf_repeat(N):
    return chain.from_iterable(repeat(i, N) for i in count(1))

Upvotes: 1

BrenBarn
BrenBarn

Reputation: 251383

You just need to call the generator object (what you called f3) at some point. You can call it when you create it:

f3 = inf_repeat(3)()

or even inside inf_repeat

# change last line to this
return func()

Putting yield in your function makes it a generator function --- that is, a function that, when called, returns a generator. If you want to get the generator, you need to call your generator function at some point.

Incidentally, your implementation is needlessly complex. You can get your desired behavior much more simply without all those decorators and nested functions:

def inf_repeat(k):
    number = k
    while True:
        yield number // k
        number += 1

Then:

>>> list(itertools.islice(inf_repeat(3), 10))
[1, 1, 1, 2, 2, 2, 3, 3, 3, 4]
>>> list(itertools.islice(inf_repeat(4), 13))
[1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4]

Upvotes: 1

Related Questions