Почему python использует «else» после циклов for и while? - PullRequest
376 голосов
/ 02 апреля 2012

Я понимаю, как работает эта конструкция:

for i in range(10):
    print(i)

    if i == 9:
        print("Too big - I'm giving up!")
        break;
else:
    print("Completed successfully")

Но я не понимаю, почему в качестве ключевого слова здесь используется else, поскольку оно предполагает, что рассматриваемый код запускается только в том случае, если блок for не завершается, что противоположно тому, что он делает! Как бы я ни думал об этом, мой мозг не может плавно перейти от оператора for к блоку else. Для меня continue или continuewith будет иметь больше смысла (и я пытаюсь научиться читать его как таковой).

Мне интересно, как Python-кодеры читают эту конструкцию в своей голове (или вслух, если хотите). Возможно, мне не хватает чего-то, что сделало бы такие кодовые блоки более легко расшифрованными?

Ответы [ 21 ]

460 голосов
/ 02 апреля 2012

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

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

Используя конструкцию Python for ... else, у вас есть

for i in mylist:
    if i == theflag:
        break
    process(i)
else:
    raise ValueError("List argument missing terminal flag.")

Сравните это с методом, который не использует этот синтаксический сахар:

flagfound = False
for i in mylist:
    if i == theflag:
        flagfound = True
        break
    process(i)

if not flagfound:
    raise ValueError("List argument missing terminal flag.")

В первом случае raise тесно связан с циклом for, с которым он работает. Во втором случае привязка не так сильна, и во время обслуживания могут возникать ошибки.

223 голосов
/ 02 апреля 2012

Это странная конструкция даже для опытных программистов Python.При использовании в сочетании с циклами for это в основном означает «найти какой-либо элемент в итерируемом элементе, в противном случае, если ничего не найдено, do ...».Как в:

found_obj = None
for obj in objects:
    if obj.key == search_key:
        found_obj = obj
        break
else:
    print('No object found.')

Но всякий раз, когда вы видите эту конструкцию, лучшей альтернативой является либо инкапсуляция поиска в функции:

def find_obj(search_key):
    for obj in objects:
        if obj.key == search_key:
            return obj

, либо использование понимания списка:

matching_objs = [o for o in objects if o.key == search_key]
if matching_objs:
    print('Found {}'.format(matching_objs[0]))
else:
    print('No object found.')

Это семантически не эквивалентно двум другим версиям, но работает достаточно хорошо в не критичном к производительности коде, где не имеет значения, перебираете ли вы весь список или нет.Другие могут не согласиться, но я бы лично не использовал блоки for-else или while-else в производственном коде.

См. Также [Python-ideas] Сводка для ... еще потоков

143 голосов
/ 20 мая 2014

Отличная презентация Раймона Хеттингера под названием Преобразование кода в красивый идиоматический Python , в которой он кратко рассказывает об истории конструкции for ... else. Соответствующий раздел - «Различение нескольких точек выхода в петлях» , начиная с 15: 50 и продолжаясь около трех минут. Вот основные моменты:

  • Конструкция for ... else была разработана Дональдом Кнутом в качестве замены для некоторых GOTO вариантов использования;
  • Повторное использование ключевого слова else имело смысл, потому что "это то, что использовал Кнут, и люди знали, что в то время все [for заявления] включали if и GOTO внизу, и они ожидали else, "
  • Оглядываясь назад, его следовало бы назвать «без перерыва» (или, возможно, «без перерыва»), и тогда это не могло бы сбить с толку. *

Итак, если вопрос таков: "Почему они не меняют это ключевое слово?" тогда Cat Plus Plus, вероятно, дал самый точный ответ - на данный момент, это будет слишком разрушительным для существующего кода, чтобы быть практичным. Но если вопрос, который вы действительно задаете, заключается в том, почему else был повторно использован в первую очередь, ну, по-видимому, в то время это казалось хорошей идеей.

Лично мне нравится компромисс комментирования # no break в строке, где else, на первый взгляд, можно ошибочно принять за принадлежность внутри цикла. Это достаточно ясно и кратко. Эта опция получает краткое упоминание в резюме, которое Бьорн связал в конце своего ответа:

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

for item in sequence:
    process(item)
else:  # no break
    suite

* Бонусная цитата из той части видео: «Точно так же, как если бы мы назвали лямбда makefunction, никто бы не спросил:« Что делает лямбда? »

31 голосов
/ 02 апреля 2012

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

20 голосов
/ 27 октября 2017

Чтобы сделать это простым, вы можете думать об этом так;

  • Если в цикле for встречается команда break, то часть else не будет вызвана.
  • Если в цикле for не встречается команда break, будет вызвана часть else.

Другими словами, если итерация цикла не "нарушена" с break, будет вызвана часть else.

15 голосов
/ 05 февраля 2015

Самый простой способ, которым я нашел «получить» то, что сделал for / else, и, что более важно, когда его использовать, это сконцентрироваться на том, куда переходит оператор break. Конструкция For / else представляет собой один блок. Перерыв выпрыгивает из блока и, таким образом, перепрыгивает через условие else. Если бы содержимое предложения else просто следовало за предложением for, оно никогда бы не перепрыгнуло, и поэтому эквивалентную логику нужно было бы предоставить, поместив его в if. Это было сказано ранее, но не совсем в этих словах, так что это может помочь кому-то еще. Попробуйте запустить следующий фрагмент кода. Я искренне поддерживаю комментарий «без перерыва» для ясности.

for a in range(3):
    print(a)
    if a==4: # change value to force break or not
        break
else: #no break  +10 for whoever thought of this decoration
    print('for completed OK')

print('statement after for loop')
14 голосов
/ 13 мая 2014

Я думаю, что в документации есть отличное объяснение else , continue

[...] оно выполняется, когда цикл завершается из-за исчерпаниясписок (с помощью for) или когда условие становится ложным (с помощью while), но не тогда, когда цикл завершается с помощью оператора break. "

Источник: Документы Python 2: руководство попоток управления

12 голосов
/ 02 апреля 2012

Я читал что-то вроде:

Если все еще находятся условия для запуска цикла, делайте что-то, else делайте что-то еще.

7 голосов
/ 12 января 2016

Поскольку техническая часть получила довольно много ответов, мой комментарий как раз связан с путаницей , которая приводит к этому переработанному ключевому слову .

Будучи Python очень красноречивым языком программирования, злоупотребление ключевым словом более печально известно. Ключевое слово else прекрасно описывает часть потока дерева решений: «если вы не можете этого сделать, (иначе) сделайте это». Это подразумевается на нашем родном языке.

Вместо этого использование этого ключевого слова с операторами while и for создает путаницу. Причина, по которой наша карьера программиста научила нас, что утверждение else находится в дереве решений; его логическая область действия , оболочка, которая условно возвращает путь для подражания. Между тем, циклические операторы имеют явно выраженную цель достичь чего-то. Цель достигается после непрерывных итераций процесса.

if / else указывает путь для следования . Петли следуют по пути, пока "цель" не будет достигнута .

Проблема в том, что else - это слово, которое четко определяет последний параметр в условии. Семантика этого слова совместно используется Python и Human Language. Но другое слово в человеческом языке никогда не используется для обозначения действий, которые кто-то или что-либо предпримет после того, как что-то будет выполнено. Он будет использоваться, если в процессе его завершения возникнет проблема (больше похожая на инструкцию break ).

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

Это напоминает ту ситуацию, в которой какой-то ребенок попадает после выполнения каждого шага сборки игрушки: И ТО Какой папа?

5 голосов
/ 07 июля 2014

Я читаю это как «Когда iterable полностью исчерпан, и выполнение собирается перейти к следующему оператору после завершения for, будет выполнено предложение else».Таким образом, когда итерация прерывается на break, это не будет выполнено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...