Reputation: 3891
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
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
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
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