Вопрос-Ответ

Why does python use 'else' after for and while loops?

Почему python использует 'else' после циклов for и while?

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

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 or continuewith имело бы больше смысла (и я пытаюсь приучить себя читать его как таковой).

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


Этот вопрос касается лежащего в основе проектного решения, т.Е. Почему полезно иметь возможность писать этот код. Смотрите также предложение Else в операторе Python while для конкретного вопроса о том, что означает синтаксис.

Переведено автоматически
Ответ 1

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

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

Используя конструкцию 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, с которым он работает. Во втором привязка не такая сильная, и при обслуживании могут возникать ошибки.

Ответ 2

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

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-идеи] Краткое описание потоков for ... else

Ответ 3

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


  • for ... else Конструкция была разработана Дональдом Кнутом в качестве замены для определенных GOTO вариантов использования;

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

  • Оглядываясь назад, это должно было называться "no break" (или, возможно, "nobreak"), и тогда это не сбивало бы с толку.*

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

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


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


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


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

Ответ 4

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


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

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

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

python