Вопрос

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

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

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

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

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

Это было полезно?

Решение

Вы нарушаете определение генератора, который должен выдавать исключение 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"

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

Другие советы

Есть несколько способов сделать это.Вы всегда можете использовать 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()

Подводя итог некоторым предыдущим ответам, это можно решить следующим образом:

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

    if empty:
        print "empty"

так что на самом деле здесь нет пункта «else».

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

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

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

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

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


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

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

def iterate(i):
    if len(i) == 0: print 'i is empty'
    else:
        for value in i:
            yield value
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top