Использование for ... еще в генераторах Python - PullRequest
7 голосов
/ 02 марта 2009

Я большой поклонник Python для ... еще синтаксиса - удивительно, как часто это применимо и насколько эффективно оно может упростить код.

Однако я не нашел хорошего способа использовать его в генераторе, например:

def iterate(i):
    for value in i:
        yield value
    else:
        print 'i is empty'

В приведенном выше примере я бы хотел, чтобы оператор print выполнялся, только если i пусто. Однако, поскольку else учитывает только break и return, он всегда выполняется независимо от длины i.

Если невозможно использовать for...else таким образом, каков наилучший подход к этому, чтобы оператор print выполнялся только тогда, когда ничего не получено?

Ответы [ 6 ]

11 голосов
/ 02 марта 2009

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

Итак:

def iterate(i):
    for value in i:
        yield value
    return

Лучше всего, чтобы вызывающий код обрабатывал случай пустого итератора:

count = 0
for value in iterate(range([])):
    print value
    count += 1
else:
    if count == 0:
        print "list was empty"

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

5 голосов
/ 02 марта 2009

Есть несколько способов сделать это. Вы всегда можете использовать Iterator напрямую:

def iterate(i):
    try:
        i_iter = iter(i)
        next = i_iter.next()
    except StopIteration:
        print 'i is empty'
        return

    while True:
        yield next
        next = i_iter.next()

Но если вы знаете больше о том, чего ожидать от аргумента i, вы можете быть более кратким:

def iterate(i):
    if i:  # or if len(i) == 0
        for next in i:
            yield next
    else:
        print 'i is empty'
        raise StopIteration()
3 голосов
/ 31 декабря 2009

Как вы заметили, for..else обнаруживает только break. Так что это применимо только тогда, когда вы ищете что-то, а затем stop .

Это не относится к вашей цели не потому, что это генератор, а , потому что вы хотите обработать все элементы, не останавливая (потому что вы хотите получить их все, но это не главное).

Итак, генератор или нет, вам действительно нужен логический тип, как в решении Бера.

3 голосов
/ 02 марта 2009

Суммируя некоторые из предыдущих ответов, это можно решить так:

def iterate(i):
    empty = True
    for value in i:
        yield value
        empty = False

    if empty:
        print "empty"

так что на самом деле здесь нет оговорки "else".

0 голосов
/ 02 марта 2009

Если это невозможно использовать для ... иначе, каков наилучший подход к этому, чтобы оператор print выполнялся только тогда, когда ничего не получено?

Максимум, что я могу придумать:


>>> empty = True
>>> for i in [1,2]:
...     empty = False
... if empty:
...     print 'empty'
...
>>>
>>>
>>> empty = True
>>> for i in []:
...     empty = False
... if empty:
...    print 'empty'
...
empty
>>>

0 голосов
/ 02 марта 2009

А как насчет простого if-else?

def iterate(i):
    if len(i) == 0: print 'i is empty'
    else:
        for value in i:
            yield value
...