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

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

27 ответов

Решение

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

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

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

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

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

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

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

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

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

Это так просто.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Мне интересно, как программисты Python читают эту конструкцию в уме (или вслух, если хотите).

Я просто думаю в голове:

"иначе не встречалось ..."

Вот и все!

Это потому, что предложение выполняется, только если оператор НЕ встречался в цикле.

Ссылка:

См. Здесь: https://book.pythontips.com/en/latest/for_-_else.html#else-clause (выделение добавлено, а «не» изменено на «НЕ»):

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

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

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

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

Для меня есть три способа "чтения" else в 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
----------

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

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

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

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

Коды в 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

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

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

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

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

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

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

Программистам это может показаться запутанным, поскольку мы всегда ожидаем увидетьifкогда мы сталкиваемся с , но в реальной жизни мы используемelseсfor

Например, я прошу своего сына поискать на книжной полке словарь и принести его мне, а если он не сможет его найти, то он должен пойти и купить его для меня.

Этоalgorithmвhisразум

      for book in bookshelf:
    if book == 'dictionary':
        #Lucky Me !! I find it lets bring it to dad
        bring('dictionary')
        break #and of course I break and stop searching 
else: #too bad :( we do not have any dictionaries
    buy('dictionary');

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

Ветвь else цикла выполняется один раз, независимо от того, входит ли цикл в свое тело или нет, за исключением случаев, когда тело цикла введено, но не завершается. То есть внутри цикла встречается оператор break или return.

      my_list = []
for i in my_list:
    print(i, end=',')
else:
    print('loop did not enter')

##################################    

for i in range(1,6,1):
    print(i, end=',')
else:
    print('loop completed successfully:', i)

##################################    

for i in range(1,6,1):
    if i == 3:
        print('loop did not finish:', i)
        break
    print(i, end=',')
else:
    print('else:', i)

Выход:

      loop did not enter
1,2,3,4,5,loop completed successfully: 5
1,2,loop did not finish: 3

То же самое для while-else.

      import random
random.seed(8)

i = 100
while i < 90:
    print(i, end=',')
    i = random.randint(0,100)
else:
    print('loop did not enter:', i)

##################################    

i = 25
while i < 90:
    print(i, end=',')
    i = random.randint(0,100)
else:
    print('loop completed successfully:', i)

##################################    

i = 25
while i < 90:
    if i % 10 == 0:
        print('loop did not finish:', i)
        break
    print(i, end=',')
    i = random.randint(0,100)
else:
    print('else:', i)

Выход:

      loop did not enter: 100
25,29,47,48,16,24,loop completed successfully: 90
25,5,loop did not finish: 10
      for i in range(10):
    print(i)

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

Ключевое слово break используется для завершения цикла. если i = 9, то цикл закончится. в то время как любые, если условия не очень удовлетворяли, то else остальное сделает.

Предложение else выполняется после нормального завершения цикла. Это означает, что блок :==>else сразу после for / while выполняется только тогда, когда цикл НЕ завершается оператором break.

      for item in lista:
if(obj == item ):
    print("if True then break will run and else not run")
    break;
else:
print("in  else => obj not fount ")

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам