Есть ли выражение для бесконечного генератора?

119

Есть ли простое выражение генератора, которое может давать бесконечные элементы?

Это чисто теоретический вопрос. Здесь нет нужды в "практическом" ответе :)


Например, легко сделать конечный генератор:

my_gen = (0 for i in xrange(42))

Однако, чтобы создать бесконечное число, мне нужно «засорить» свое пространство имен ложной функцией:

def _my_gen():
    while True:
        yield 0
my_gen = _my_gen()

Выполнение действий в отдельном файле и import-ing позже не считается.


Я также знаю, что itertools.repeatэто именно так. Мне любопытно, есть ли однострочное решение без этого.

hugomg
источник
9
На самом деле вам не нужно загрязнять пространство имен ... просто назовите функцию my_genи сделайте это my_gen = my_gen().
6502
2
вы также можете использовать, del _my_genесли не хотите путать их
Джон Ла Рой

Ответы:

133
for x in iter(int, 1): pass
  • Два аргумента iter= вызываемый без аргументов + контрольное значение
  • int() всегда возвращается 0

Следовательно, iter(int, 1)- бесконечный итератор. Очевидно, существует огромное количество вариаций этой конкретной темы (особенно если вы добавите lambdaих в микс). Один вариант особого примечания заключается в том iter(f, object()), что использование только что созданного объекта в качестве контрольного значения почти гарантирует бесконечный итератор независимо от вызываемого объекта, используемого в качестве первого аргумента.

ncoghlan
источник
3
очень интересный способ использования iterсвойства, о intкотором мы много раз забываем.
Senthil Kumaran
3
вы можете использовать этот волшебный рецепт для имитации itertools.count:count = lambda start=0, step=1: (start + i*step for i, _ in enumerate(iter(int, 1)))
Coffee_Table
1
Просто объяснить , что здесь происходит: Когда iter-функции вызывается с двумя аргументами, она ведет себя немного иначе , чем обычно: iter(callable, sentinel) -> iterator. Аргумент 1 callableвызывается для каждой итерации итератора, пока он не вернет значение sentinel. Однако, как int()всегда будет возвращаться 0, мы можем назвать int()вечно и никогда не достигнет 1. Это будет фактически производить бесконечный список 0«s
Olsgaard
217

itertools предоставляет три бесконечных генератора:

Я не знаю других в стандартной библиотеке.


Поскольку вы просили однострочный:

__import__("itertools").count()
Katriel
источник
18
Re: repeat (x, times = ∞) - нет символа для того, кто задался вопросом - отсутствие аргумента приводит к повторному запуску навсегда
Mr_and_Mrs_D 02
Проголосовали, потому что (в то время как ответ ncoghlan напрямую касается вопроса OP) это более широко применимо.
Huw Walters
Это чертовски удобнее, чем iter(int, 1)заклинание. Жаль, itertoolsчто нет endlessly()метода, единственная цель которого - это делать; itertools.count()тоже не все так читабельно.
BallpointBen
19

вы можете перебирать вызываемый объект, возвращая константу, всегда отличную от дозорной iter ()

g1=iter(lambda:0, 1)
user237419
источник
8
Я и люблю, и ненавижу это ... Мне нравится, что он выполняет то, что я хочу, в таком небольшом количестве персонажей, но ненавижу то, что никто никогда не будет смотреть на это и знать, что он должен делать.
ArtOfWarfare
1
зная синтаксис iter(здесь с дополнительным часовым) и синтаксис lambda(здесь без каких-либо переданных параметров, просто return 0), единственное место, где можно ненавидеть, - это таинственность g1.
Славомир Ленарт
@ SławomirLenart мужчины никогда этого не понимают. просто оно было до неприличия маленьким, поэтому я сквирнул 1 г.
user237419
8

Ваша ОС может предоставить что-то, что можно использовать как бесконечный генератор. Например, на linux

for i in (0 for x in open('/dev/urandom')):
    print i

очевидно, это не так эффективно, как

for i in __import__('itertools').repeat(0)
    print i
Джон Ла Рой
источник
11
Решение / dev / urandom зависит от того, \nпоявляются ли они время от времени ... Коварный! :)
hugomg
5

Ни один, который внутренне не использует другой бесконечный итератор, определенный как класс / функция / генератор (не -выражение, функция с yield). Выражение генератора всегда извлекается из итерируемого объекта anoter и ничего не делает, кроме фильтрации и сопоставления своих элементов. Вы не можете перейти от конечных элементов к бесконечным с помощью только mapи filter, вам нужно while(или, forчто не завершается, а это именно то, что мы не можем иметь, используя только forи конечные итераторы).

Интересный факт: PEP 3142 внешне похож, но при ближайшем рассмотрении кажется, что он по-прежнему требует forпункта (так что нет (0 while True)для вас), т.е. предоставляет только ярлык для itertools.takewhile.


источник
Как я и подозревал ... Можем ли мы быть уверены, что нет готового бесконечного генератора, которым можно было бы злоупотреблять? (К сожалению, xrange (0,1, -1) не работает ...)
hugomg
2
@missingno: from itertools import repeat, count, cycleвероятно, считается «легко доступным» для большинства людей.
ncoghlan
1
Ой, я забыл про 2-аргумент iter. Бесконечные итераторы фактически доступны как встроенные - см. Мой ответ :)
ncoghlan
5

Довольно уродливо и безумно (однако, очень забавно), но вы можете создать свой собственный итератор из выражения, используя некоторые уловки (без "загрязнения" вашего пространства имен по мере необходимости):

{ print("Hello world") for _ in
    (lambda o: setattr(o, '__iter__', lambda x:x)
            or setattr(o, '__next__', lambda x:True)
            or o)
    (type("EvilIterator", (object,), {}))() } 
Томас Баручель
источник
Вы явно любите LISP
Faissaloo
1
@Faissaloo В самом деле ... Вы можете найти еще более безумное выражение на старой странице, которую я написал: baruchel.github.io/python/2018/06/20/…
Thomas Baruchel
2

Возможно, вы могли бы использовать такие декораторы, например:

def generator(first):
    def wrap(func):
        def seq():
            x = first
            while True:
                yield x
                x = func(x)
        return seq
    return wrap

Использование (1):

@generator(0)
def blah(x):
    return x + 1

for i in blah():
    print i

Использование (2)

for i in generator(0)(lambda x: x + 1)():
    print i

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

julx
источник
9
OP запрашивает одинарный лайнер, а вы представляете декоратор из 10 строк с тройным вложением defи закрытием? ;)
2
@delnan Хорошо, но если вы определите декоратор один раз, у вас будет свой однострочник, не так ли? Насколько я понимаю, цель состоит в том, чтобы каждый дополнительный бесконечный генератор был реализован в одной строке. И это то, что здесь представлено. Вы можете иметь (2^x), можете иметь (x). Если вы немного улучшите его, возможно, также фибоначчи и т. Д.
julx
Не отвечает на мой вопрос, но как же тогда вам не полюбить все эти пушистые застежки? Кстати, я почти уверен, что вы можете избавиться от лишних скобок, избавившись от seqкода и wrap
сделав