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

482

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

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

Кент Бугаарт
источник
26
Возможно, вы захотите перевести это «тогда» в своей голове.
Марчин
63
Не забывайте ключевую строку в Zen of Python: «... сначала этот путь может быть неочевидным, если вы не голландец».
Даниэль Роузман
51
В своей голове я перевожу это «если не сломать» . И, поскольку breakон часто используется в циклах «Я нашел это» , вы можете перевести его на «если не найдено» , что недалеко от того, что elseчитается
MestreLion
29
Я думаю, что реальный вопрос, который возникает у многих здесь: «В чем разница for ... else foo()и просто ставить foo()после цикла for?» И ответ в том, что они ведут себя по-разному, только если цикл содержит break(как подробно описано ниже).
Сэм Кауффман
10
Точка с запятой в питоне ... мои глаза болят .. хотя это синтаксически правильно, это не очень хорошая практика
DarkCygnus

Ответы:

278

Это странная конструкция даже для опытных программистов 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] Краткое содержание для ... еще потоков

Бьорн Линдквист
источник
50
Понимание списка - неправильная однострочность. Если вы ищете один элемент, как в forпримерах цикла, и хотите использовать выражение выражения / списка генератора, то вы хотите next((o for o in objects if o.key == search_key), None)или обернуть его в try/ exceptи не использовать значение по умолчанию вместо if/ else.
2012 г.,
4
и, как и ответ Ланса Хелстена, есть реальные случаи, когда лучше использовать for/elseконструкцию.
Андре
5
Приветствия. У меня был файл с плохим отступом, где я elseполучил пару forи я понятия не имел, что это законно.
maxywb
3
Я думаю, что цикл for является наиболее очевидной из конструкций там.
Майлз Рут
14
Стоит отметить, что предложение else будет выполняться, даже если цикл for имеет значения, если только breakоператор не является явным запуском, как в этом примере. Из документов выше: «В elseпредложении есть еще одна проблема: если breakв цикле его нет, elseпредложение функционально избыточно». напр.for x in [1, 2, 3]:\n print x\n else:\n print 'this executes due to no break'
дхакнер
587

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

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

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

Лэнс Хельстен
источник
69
Это объясняет это лучше, чем выбранный ответ, где автор на самом деле не понимает, о чем еще!
erikbwork
17
Я бы сказал, что этот синтаксический сахар может испортить зубы вашего проекта. Это не сделало бы Python: the good partsкнигу.
кодек
1
Можете ли вы подтвердить, что в вашем примере это process(i)происходит для каждого элемента mylistстрого раньше theflag, а не с theflagсамим собой? Это то, что было задумано?
ли
4
processбудет выполняться для каждого, iкоторый существует в списке до theflagего достижения, он не будет выполняться для элементов в списке после theflag, и он не будет выполняться для theflag.
Ланс Хельстен
1
оператор else также выполняется, если в итерируемом элементе нет элементов
Lost Crotchet
173

Есть отличная презентация Раймонда Хеттингера под названием « Преобразование кода в красивый идиоматический 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

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

Воздуха
источник
33

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

Cat Plus Plus
источник
2
Похоже finally, было бы лучшим выбором в этом случае. Не было ли еще ключевого слова finally во время введения этой конструкции?
Ponkadoodle
26
@Wallacoloo finallyне намного лучше, потому что он подразумевает, что блок всегда будет выполняться после цикла, и это не так (потому что это было бы избыточно, если просто поместить код для запуска после цикла).
Cat Plus Plus
Этого также не может быть, finallyпотому что условие else выполняется также, когда continueиспользуется в цикле for - это возможно много раз, а не только в конце.
pepr
6
На elseвыполнение условия @pepr не влияют continue( документы и тестовый код )
Air
@AirThomas: +1. Вы правы. elseВыполняется только тогда , когда continueбыл один для последней итерации.
pepr
33

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

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

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

Ad Infinitum
источник
elseБлок также не будет выполнен , если тело цикла вызывает исключение.
Амаль К
17

Самый простой способ, которым я нашел «получить» то, что сделал 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')
Neil_UK
источник
«Перерыв выпрыгивает из блока и, таким образом, перепрыгивает« через »условие else» - хотя это может быть полезно как способ «получить» for:/ else:, на самом деле это не дает оправдания ключевому слову else. Учитывая приведенную здесь структуру, then:кажется, что это было бы гораздо более естественным. (Там являются причины elseбыть выбраны, приведенные в других ответах - они просто не предусмотрены здесь.)
Марк Эмери
16

Я думаю , что документация имеет большое объяснение еще , по- прежнему

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

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

Аян
источник
13

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

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

pcalcao
источник
Вы по- прежнему на условиях полезны (+1), хотя это неправильно - это человек ;-)
Wolf
-1; это произношение for:/ else:делает его звучащим так, как будто оно else:всегда запускается после цикла, что не так.
Марк Амери
11

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

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

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

if / else указать путь для подражания . Петли следуют по пути, пока «цель» не будет достигнута .

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

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

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

3rdWorldCitizen
источник
Я думаю, что этот ответ решает проблему путаницы, о которой говорил ОП. Ключевое слово else абсолютно противоположно тому, что вы ожидаете от английского значения else, когда оно присоединено к действию for. Теоретически, for ... else мог бы работать иначе, потому что вы оказались бы в части else, когда цикл разорван, но проблема в том, чтобы использовать его для поиска элемента x и обработки случая, когда x равен не найден, вам, возможно, придется использовать флаг или другой тест после всего для .. еще конструкции
Spacen Jasset
7

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

0xc0de
источник
6

Я согласен, это больше похоже на 'elif not [условие (и), повышающее разрыв]'.

Я знаю, что это старая ветка, но сейчас я изучаю тот же вопрос, и я не уверен, что кто-то получил ответ на этот вопрос так, как я его понимаю.

Для меня есть три способа «чтения» операторов elsein For... elseили While... else, все из которых эквивалентны:

  1. else == if the loop completes normally (without a break or error)
  2. else == if the loop does not encounter a break
  3. else == else not (condition raising break) (предположительно, есть такое условие, иначе у вас не будет петли)

Таким образом, по сути, «else» в цикле - это действительно «elif ...», где «...» - это (1) без перерыва, что эквивалентно (2) NOT [условие (я), поднимающее разрыв].

Я думаю, что ключ в том, что elseбез «перерыва» бессмысленно, так что for...elseвключает в себя:

for:
    do stuff
    conditional break # implied by else
else not break:
    do more stuff

Итак, основными элементами for...elseцикла являются следующие, и вы бы прочитали их на простом английском языке как:

for:
    do stuff
    condition:
        break
else: # read as "else not break" or "else not condition"
    do more stuff

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

пример

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

for x in range(0,3):
    print("x: {}".format(x))
    if x == 2:
        try:
            raise AssertionError("ASSERTION ERROR: x is {}".format(x))
        except:
            print(AssertionError("ASSERTION ERROR: x is {}".format(x)))
            break
else:
    print("X loop complete without error")

Результат

x: 0
x: 1
x: 2
ASSERTION ERROR: x is 2
----------
# loop not completed (hit break), so else didn't run

пример

Простой пример с разрывом удара.

for y in range(0,3):
    print("y: {}".format(y))
    if y == 2: # will be executed
        print("BREAK: y is {}\n----------".format(y))
        break
else: # not executed because break is hit
    print("y_loop completed without break----------\n")

Результат

y: 0
y: 1
y: 2
BREAK: y is 2
----------
# loop not completed (hit break), so else didn't run

пример

Простой пример, где нет перерыва, нет условия, поднимающего перерыв, и нет ошибки.

for z in range(0,3):
     print("z: {}".format(z))
     if z == 4: # will not be executed
         print("BREAK: z is {}\n".format(y))
         break
     if z == 4: # will not be executed
         raise AssertionError("ASSERTION ERROR: x is {}".format(x))
else:
     print("z_loop complete without break or error\n----------\n")

Результат

z: 0
z: 1
z: 2
z_loop complete without break or error
----------
NotAnAmbiTurner
источник
6

elseКлючевое слово может сбивать с толку здесь, и , как многие люди отмечали, что - то вроде nobreak, notbreakболее уместно.

Для того , чтобы понять for ... else ...логически, сравнить его с try...except...else, а не if...else...большинство питона программистов знакомы со следующим кодом:

try:
    do_something()
except:
    print("Error happened.") # The try block threw an exception
else:
    print("Everything is find.") # The try block does things just find.

Точно так же думайте breakкак особый вид Exception:

for x in iterable:
    do_something(x)
except break:
    pass # Implied by Python's loop semantics
else:
    print('no break encountered')  # No break statement was encountered

Разница pythonподразумевается except breakи вы не можете ее выписать, поэтому она становится:

for x in iterable:
    do_something(x)
else:
    print('no break encountered')  # No break statement was encountered

Да, я знаю, что это сравнение может быть трудным и утомительным, но оно проясняет путаницу.

cizixs
источник
При копировании с него вы должны сделать ссылку на ресурс: заметки Ника Коглана из Python .
Годайго
@годайго спасибо за ссылку. Я прочитал и принял концепцию при первом изучении Python, не запомнил источник при написании ответа.
cizixs
@cizixs Вы «не запомнили источник», а просто включили целые предложения комментариев, идентичные оригиналу? Ooookaaaay.
Марк Эмери
5

Коды в elseблоке операторов будут выполняться, когда forцикл не был прерван.

for x in xrange(1,5):
    if x == 5:
        print 'find 5'
        break
else:
    print 'can not find 5!'
#can not find 5!

Из документов: ломайте и продолжайте утверждения, а также пункты о циклах

Операторы цикла могут иметь предложение else; он выполняется, когда цикл завершается из-за исчерпания списка (с помощью for) или когда условие становится ложным (с помощью while), но не тогда, когда цикл завершается оператором break. Это иллюстрируется следующим циклом, который ищет простые числа:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print(n, 'equals', x, '*', n//x)
...             break
...     else:
...         # loop fell through without finding a factor
...         print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

(Да, это правильный код. Посмотрите внимательно: предложение else принадлежит циклу for, а не оператору if.)

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

Оператор continue, также заимствованный из C, продолжает следующую итерацию цикла:

>>> for num in range(2, 10):
...     if num % 2 == 0:
...         print("Found an even number", num)
...         continue
...     print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9
GoingMyWay
источник
1
Это ничего не добавляет и не отвечает на вопрос, который не так, как, но почему .
эфир
5

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

Во-первых, помните, что циклы for - это просто синтаксический сахар вокруг циклов while. Например, цикл

for item in sequence:
    do_something(item)

можно переписать (примерно) как

item = None
while sequence.hasnext():
    item = sequence.next()
    do_something(item)

Во-вторых, помните, что циклы while - это просто повторяющиеся блоки if! Вы всегда можете прочитать цикл while как «если это условие истинно, выполнить тело, а затем вернуться и проверить снова».

Так что, хотя / else имеет смысл: это та же структура, что и if / else, с добавленной функциональностью зацикливания до тех пор, пока условие не станет ложным, вместо простой проверки условия один раз.

И тогда for / else также имеет смысл: поскольку все циклы for являются просто синтаксическим сахаром поверх циклов while, вам просто нужно выяснить, каково неявное условное выражение базового цикла while, а затем else соответствует тому, когда состояние становится ложным.

Аарон Гейбл
источник
4

Великолепные ответы:

  • это, которые объясняют историю, и
  • это дает правильное цитирование, чтобы облегчить ваш перевод / понимание.

Моя заметка здесь взята из того, что однажды сказал Дональд Кнут (извините, не могу найти ссылку), что существует конструкция, в которой while-else неотличима от if-else, а именно (в Python):

x = 2
while x > 3:
    print("foo")
    break
else:
    print("boo")

имеет такой же поток (исключая низкие разности уровней) как:

x = 2
if x > 3:
    print("foo")
else:
    print("boo")

Дело в том, что if-else можно рассматривать как синтаксический сахар для while-else, который неявно присутствует breakв конце своего ifблока. Противоположный вывод, что whileцикл является расширением if, является более распространенным (это просто повторная / циклическая условная проверка), потому что ifчасто учат раньше while. Однако это не так, потому что это будет означать, что elseблок while-else будет выполняться каждый раз когда условие ложно.

Чтобы облегчить ваше понимание, подумайте об этом так:

Без break, returnи т. Д. Цикл заканчивается только тогда, когда условие больше не elseвыполняется, и в этом случае блок также будет выполнен один раз. В случае Python forвы должны рассмотреть forциклы в стиле C (с условиями) или перевести их на while.

Еще одна заметка:

Преждевременное break, returnи т.д. внутри цикла делает невозможным условие станет ложным , поскольку выполнение выпрыгнул из цикла в то время как условие было верно , и это никогда не вернется , чтобы проверить его снова.

WloHu
источник
3

Вы можете думать об этом как, elseкак в остальной части материала, или о других вещах, которые не были сделаны в цикле.

jamylak
источник
3
for i in range(3):
    print(i)

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

«остальное» здесь безумно просто, просто значит

1, «если for clauseзавершено»

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

    if i == 2:
        print("Too big - I'm giving up!")
        break;
if "for clause is completed":
    print("Completed successfully")

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

else вот если бы по своей природе.

2, однако, как насчет for clause is not run at all

In [331]: for i in range(0):
     ...:     print(i)
     ...: 
     ...:     if i == 9:
     ...:         print("Too big - I'm giving up!")
     ...:         break
     ...: else:
     ...:     print("Completed successfully")
     ...:     
Completed successfully

Так что это полностью утверждение логической комбинации:

if "for clause is completed" or "not run at all":
     do else stuff

или скажем так:

if "for clause is not partially run":
    do else stuff

или так:

if "for clause not encounter a break":
    do else stuff
Исчисление
источник
иначе действует как «транзакция» в SQL.
Исчисление
2

Вот еще один идиоматический вариант использования помимо поиска. Допустим, вы хотели дождаться выполнения условия, например, открытия порта на удаленном сервере, а также некоторого времени ожидания. Тогда вы можете использовать такую while...elseконструкцию:

import socket
import time

sock = socket.socket()
timeout = time.time() + 15
while time.time() < timeout:
    if sock.connect_ex(('127.0.0.1', 80)) is 0:
        print('Port is open now!')
        break
    print('Still waiting...')
else:
    raise TimeoutError()
Джонатан Судиаман
источник
1

Я просто пытался понять это снова сам. Я обнаружил, что следующее помогает!

• Думайте о том, elseчто он связан с ifвнутренним циклом (а не с for) - если условие выполнено, прервите цикл, иначе сделайте это - за исключением того, что он elseсвязан с несколькими ifs!
• Если никакие ifs не были удовлетворены вообще, тогда сделайте else.
• Множество ifs также может рассматриваться как if- elifs!

Джермейн Го
источник
-2

Я рассматриваю структуру как для (если) A else B, так и для (if) -sese является специальным if-else , примерно . Это может помочь понять другое .

A и B выполняются не более одного раза, что аналогично структуре if-else.

for (if) может рассматриваться как особый if, который выполняет цикл для выполнения условия if. Как только условие if выполнено, A и перерыв ; Остальное , Б.

Цзе Чжан
источник
-2

Python использует циклы else after for и while, так что если к циклу ничего не применяется, происходит что-то еще. Например:

test = 3
while test == 4:
     print("Hello")
else:
     print("Hi")

Вывод будет «Привет» снова и снова (если я прав).

Mrmongoose64
источник