Функция пустого генератора Python

В python можно легко определить функцию итератора, поставив ключевое слово yield в тело функции, например:

def gen():
    for i in range(100):
        yield i

Как я могу определить функцию генератора, которая не дает значения (генерирует 0 значений), следующий код не работает, поскольку python не может знать, что он должен быть генератором, а не нормальной функцией:

def empty():
    pass

Я мог бы сделать что-то вроде

def empty():
    if False:
        yield None

Но это было бы очень уродливо. Есть ли хороший способ реализовать пустую итераторную функцию?

Ответ 1

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

>>> def f():
...     return
...     yield
... 
>>> list(f())
[]

Я не уверен, что это намного лучше, чем у вас - он просто заменяет оператор no-op if оператором no-op 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 более читабельны и поддерживаются.

Ответ 2

iter(())

Вам не нужен генератор. Давай ребята!

Ответ 3

Python 3.3 (потому что я нахожусь на пике yield from, а потому, что @senderle украл мою первую мысль):

>>> def f():
...     yield from ()
... 
>>> list(f())
[]

Но я должен признать, что мне сложно найти пример использования, для которого iter([]) или (x)range(0) не будет работать одинаково хорошо.

Ответ 4

Другой вариант:

(_ for _ in ())

Ответ 5

Я предпочитаю следующее:

def foo():
  raise StopIteration()
  yield

"Выход" превращает его в генератор, в то время как исключение означает, что "Нет" не включен в результат (чисто пустой результат).

Ответ 6

Должна ли быть функция генератора? Если нет, как насчет

def f():
    return iter([])

Ответ 7

"Стандартный" способ сделать пустой итератор выглядит как iter ([]). Я предложил сделать [] аргумент по умолчанию для iter(); это было отклонено с хорошими аргументами, см. http://bugs.python.org/issue25215 - Jurjen

Ответ 8

generator = (item for item in [])

Ответ 9

Для тех из вас, которым на самом деле нужна функция и на самом деле нужен генератор

empty = lambda: (_ for _ in ())