2015-09-25 2 views
2

Я пытаюсь написать бесконечный генератор, который будет повторять каждое положительное целое n раз. Так, например, если я создаю f = inf_repeat(3), выводя вывод f 10 раз приведет:Пытается создать объект генератора, но получая объект функции, который не отвечает на вызовы генератора

1 1 1 2 2 2 3 3 3 4 

Я близко, но не совсем там. Вот что у меня есть:

# 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 

Моя проблема в том, что это не ведет себя как итератор. Следующие команды работают:

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

Но это раздражает, чтобы вызвать f3 с скобками. Я хотел бы иметь возможность использовать стандартный синтаксис итератора, что я видел, такие как:

print(f3.next()) 

и

new_list = [iter(f3)]*5 

Что мне нужно сделать, чтобы изменить в моей функции, чтобы добраться до этой точки ? Глядя на различные обучающие программы генератора, казалось, что yield было достаточно для создания генератора, но это явно не так.

Также у меня нет цели использовать модуль. Я проверил itertools, но, возможно, я пропустил что-то, что могло бы сделать то, что я хочу, без всего этого кода?

+0

Что делать, если вы просто делаете 'f3 = inf_repeat (3)()'? Это достаточно хорошо для вас? – BrenBarn

ответ

1

Вам просто нужно вызвать объект-генератор (то, что вы назвали f3) в какой-то момент. Вы можете назвать это, когда вы создаете ее:

f3 = inf_repeat(3)() 

или даже внутри inf_repeat

# change last line to this 
return func() 

yield Ввод в функции делает это функция генератора --- то есть функция, которая при вызове, возвращает генератор. Если вы хотите получить генератор, вам нужно вызвать функцию генератора в какой-то момент.

Кстати, ваша реализация излишне сложна.Вы можете получить желаемое поведение гораздо проще без всех этих декораторов и вложенных функций:

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

Тогда:

>>> 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] 
+0

большое спасибо. Также спасибо за ваш переработанный пример. По какой-то причине я был обеспокоен тем, что разные генераторы используют свойства, но вы показали мне, как генератор - это просто функция. Каждый из них является его собственным вызовом функции, поэтому мне не нужно беспокоиться о перекрытии, по-видимому? Вам нужно будет больше узнать об этом. – sunny

+0

@sunny: Правильно, если вы вызываете 'inf_repeat' несколько раз, результатом каждого вызова является отдельный генератор с собственным состоянием (т. Е. Его собственные« статические »локальные переменные). Он не будет делиться состоянием с чем-либо еще, если только это ссылается на глобальные переменные или внешние объекты, которые вы передаете в качестве аргументов или что-то в этом роде. – BrenBarn

1

Вот простое решение:

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

Вот решение, использующее itertools :

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

Почему бы не использовать 'for f in inf_repeat (3): print f'? – Jkdc

+0

@Jkdc: Потому что' inf_repeat' бесконечен, так что это будет бесконечный цикл – BrenBarn

+0

Это должно работать, но программа никогда не закончится, и я ясно даю понять, что OP может называть '.next()' по своему желанию. –

1

Другое решение с использованием 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 

производит

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

это действительно приятно. – sunny

Смежные вопросы