Почему python использует "else" после циклов while и 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 или continuewith будет иметь больший смысл (и я пытаюсь научиться его читать как таковой).

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

Ответ 1

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

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

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

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

Ответ 4

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

Ответ 5

Самый простой способ, который я нашел, чтобы "получить" то, что сделал 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')

Ответ 6

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

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

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

Ответ 7

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

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

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

Ответ 8

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

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

Ответ 9

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

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

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

if / else указывают путь к следующему. Циклы идут по пути, пока не будет завершена "цель".

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

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

Это похоже на ту ситуацию, когда какой-то ребенок после каждого шага сборки игрушки: И THEN, что папа?

Ответ 10

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

Ответ 11

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

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

Для меня существует три способа "чтения" операторов else в For... else или While... else, все из которых эквивалентны:

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

Итак, по сути, "else" в цикле действительно является "elif...", где "..." (1) нет разрыва, что эквивалентно (2) NOT [condition ( s) повышение разрыва].

Я думаю, что ключ состоит в том, что else бессмыслен без "break", поэтому a 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 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
----------

Ответ 12

Ключевое слово 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 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

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

Ответ 13

Коды в блоке оператора 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!

Из docs: break и continue Statement, а также Clauses on Loops

Операторы цикла могут иметь условие else; он выполняется, когда цикл завершается исчерпанием списка (с for) или когда условие становится ложным (с показом), но не тогда, когда цикл завершается оператором 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: предложение try statement else выполняется, когда не возникает исключение, и предложение цикла else выполняется, когда разрыв не происходит. Дополнительные сведения о выполнении и исключениях см. В разделе Обработка исключений.

Оператор 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

Ответ 14

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

Ответ 15

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

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

for item in sequence:
    do_something(item)

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

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

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

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

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

Ответ 16

Вот еще один идиоматический вариант использования помимо поиска. Допустим, вы хотели дождаться выполнения условия, например, открытия порта на удаленном сервере, а также некоторого времени ожидания. Тогда вы можете использовать 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()

Ответ 17

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

Ответ 18

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

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

Моя заметка здесь взята из того, что однажды сказал Дональд Кнут (извините, не могу найти ссылку), что существует конструкция, в которой 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 блок else в while-else будет выполняться каждый раз, когда условие ложно.

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

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

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

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

Ответ 19

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

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

Ответ 20

while the_condition is True:
    do_something()
else:  # when the_condition is not True any more
    do_something_else()

Ответ 21

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

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

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

Ответ 22

Допустим, у нас есть функция

def broken(x) : return False if x==5 else True

Это означает, что только 5 не сломан. Теперь в случае поломки никогда не оценивается с 5: -

for x in range(4):
    if not broken(x) : break
else:
    print("Everything broken... doom is upon us")

Даст вывод: -

Everything broken... doom is upon us

Где, когда сломано оценивается с 5: -

for x in range(6):
    if not broken(x) : break
else:
    print("Everything broken... doom is upon us")

Он не будет ничего печатать. Таким образом, косвенно говоря, есть по крайней мере то, что не сломано.

Однако, если вы хотите обмануть и пропустить что-то, что вы нашли, было сломано. То есть продолжайте цикл, даже если вы нашли 5 как прерванный, еще будет напечатано утверждение else. То есть :-

for x in range(6):
    if not broken(x) : continue
else:
    print("Everything broken... doom is upon us")

Будет печатать

Everything broken... doom is upon us

Я надеюсь, что это устранит путаницу, а не создаст новую :-)

Ответ 23

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

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

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