Вы можете использовать return
один раз в генераторе; он останавливает итерацию, ничего не возвращая, и, таким образом, предоставляет явную альтернативу выходу функции за пределы области видимости. Поэтому используйте, yield
чтобы превратить функцию в генератор, но предшествуйте ей, return
чтобы завершить работу генератора, прежде чем что-либо даст.
>>> def f():
... return
... yield
...
>>> list(f())
[]
Я не уверен, что это намного лучше, чем то, что у вас есть - он просто заменяет оператор без операции на if
оператор без операции yield
. Но это более идиоматично. Обратите внимание, что просто использование yield
не работает.
>>> def f():
... yield
...
>>> list(f())
[None]
Почему бы просто не использовать iter(())
?
Этот вопрос конкретно касается пустой функции генератора . По этой причине я считаю, что это вопрос о внутренней согласованности синтаксиса Python, а не о том, как лучше всего создать пустой итератор в целом.
Если на самом деле вопрос заключается в том, как лучше всего создать пустой итератор, то вы можете согласиться с Zectbumo в использовании iter(())
вместо него. Однако важно отметить, что iter(())
функция не возвращается! Он напрямую возвращает пустой итерируемый объект. Предположим, вы работаете с API, который ожидает вызываемого объекта, который возвращает итерацию при каждом вызове, как и обычная функция генератора. Вам нужно будет сделать что-то вроде этого:
def empty():
return iter(())
(Следует отметить, что Unutbu дал первую правильную версию этого ответа.)
Теперь вы можете найти вышесказанное более ясным, но я могу представить себе ситуации, в которых это было бы менее ясно. Рассмотрим этот пример длинного списка (надуманных) определений функций генератора:
def zeros():
while True:
yield 0
def ones():
while True:
yield 1
...
В конце этого длинного списка я бы предпочел увидеть что-нибудь со yield
знаком, например:
def empty():
return
yield
или, в Python 3.3 и выше (как предлагает DSM ), это:
def empty():
yield from ()
Наличие yield
ключевого слова дает понять с первого взгляда, что это просто еще одна функция генератора, точно такая же, как и все остальные. Чтобы увидеть, что iter(())
версия делает то же самое, требуется немного больше времени .
Это небольшая разница, но я честно считаю, что yield
функции, основанные на -основе, более удобочитаемы и удобнее.
См. Также этот отличный ответ от user3840170, который dis
показывает еще одну причину, почему этот подход предпочтительнее: он выдает наименьшее количество инструкций при компиляции.
if False: yield
но все же немного сбивает с толку людей, которые не знают эту закономерностьitertools.empty()
.return
внутри генераторов означает нечто иное. Это больше похожеbreak
.False
.Вам не нужен генератор. Ну же, ребята!
источник
iter([])
за того простого факта, что он()
является константой, в то время как он[]
может создавать экземпляр нового объекта списка в памяти при каждом его вызове.empty = lambda: iter(())
илиdef empty(): return iter(())
.tuple_iterator
вместоgenerator
. Если у вас есть случай, когда ваш генератор ничего не должен возвращать, не используйте этот ответ.Python 3.3 (потому что я в
yield from
восторге и потому что @senderle украл мою первую мысль):>>> def f(): ... yield from () ... >>> list(f()) []
Но я должен признать, что мне сложно придумать вариант использования для этого, который
iter([])
или(x)range(0)
не работал бы одинаково хорошо.источник
return; yield
илиif False: yield None
.iter([])
или(x)range(0)
не работал бы одинаково хорошо». -> Не уверен, что(x)range(0)
есть, но вариантом использования может быть метод, который должен быть переопределен с помощью полномасштабного генератора в некоторых из наследующих классов. Для обеспечения согласованности вы бы хотели, чтобы даже базовый генератор, от которого наследуются другие, возвращал генератор, как и те, которые его переопределяют.Другой вариант:
(_ for _ in ())
источник
Generator[str, Any, None]
Как сказал @senderle, используйте это:
def empty(): return yield
Я пишу этот ответ в основном для того, чтобы поделиться еще одним его оправданием.
Одна из причин выбора этого решения над другими заключается в том, что оно оптимально для интерпретатора.
>>> import dis >>> def empty_yield_from(): ... yield from () ... >>> def empty_iter(): ... return iter(()) ... >>> def empty_return(): ... return ... yield ... >>> def noop(): ... pass ... >>> dis.dis(empty_yield_from) 2 0 LOAD_CONST 1 (()) 2 GET_YIELD_FROM_ITER 4 LOAD_CONST 0 (None) 6 YIELD_FROM 8 POP_TOP 10 LOAD_CONST 0 (None) 12 RETURN_VALUE >>> dis.dis(empty_iter) 2 0 LOAD_GLOBAL 0 (iter) 2 LOAD_CONST 1 (()) 4 CALL_FUNCTION 1 6 RETURN_VALUE >>> dis.dis(empty_return) 2 0 LOAD_CONST 0 (None) 2 RETURN_VALUE >>> dis.dis(noop) 2 0 LOAD_CONST 0 (None) 2 RETURN_VALUE
Как мы видим, у
empty_return
него точно такой же байт-код, что и у обычной пустой функции; остальные выполняют ряд других операций, которые в любом случае не меняют поведения. Единственная разница междуempty_return
иnoop
заключается в том, что у первого установлен флаг генератора:>>> dis.show_code(noop) Name: noop Filename: <stdin> Argument count: 0 Positional-only arguments: 0 Kw-only arguments: 0 Number of locals: 0 Stack size: 1 Flags: OPTIMIZED, NEWLOCALS, NOFREE Constants: 0: None >>> dis.show_code(empty_return) Name: empty_return Filename: <stdin> Argument count: 0 Positional-only arguments: 0 Kw-only arguments: 0 Number of locals: 0 Stack size: 1 Flags: OPTIMIZED, NEWLOCALS, GENERATOR, NOFREE Constants: 0: None
Конечно, сила этого аргумента очень зависит от конкретной реализации Python в использовании; достаточно умный альтернативный интерпретатор может заметить, что другие операции не дают ничего полезного, и оптимизировать их. Однако даже если такие оптимизации присутствуют, они требуют, чтобы интерпретатор потратил время на их выполнение и защитился от сбоев в предположениях оптимизации, например
iter
, отскок идентификатора в глобальной области видимости на что-то еще (даже если это, скорее всего, укажет на ошибку, если он на самом деле произошло). В случаеempty_return
с оптимизацией просто нечего, поэтому даже относительно наивный CPython не будет тратить время на какие-либо ложные операции.источник
yield from ()
? (См. Ответ DSM .)Это должна быть функция генератора? Если нет, как насчет
def f(): return iter(())
источник
«Стандартный» способ сделать пустой итератор - iter ([]). Я предложил сделать [] аргументом по умолчанию для iter (); это было отклонено с хорошими аргументами, см. http://bugs.python.org/issue25215 - Jurjen
источник
generator = (item for item in [])
источник
Я хочу привести пример на основе классов, поскольку мы еще ничего не предлагали. Это вызываемый итератор, который не генерирует никаких элементов. Я считаю, что это простой и наглядный способ решения проблемы.
class EmptyGenerator: def __iter__(self): return self def __next__(self): raise StopIteration >>> list(EmptyGenerator()) []
источник