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

17 ответов


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

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

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

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

* бонус цитата из этой части видео: "так же, как если бы мы назвали лямбда makefunction,


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


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

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

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


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

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

источник: Python 2 docs: учебник по потоку управления


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

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

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

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


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

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

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

if / else указать путь. Петли следуйте по пути, пока "цель" не будет завершена.

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

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

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


Я читаю это как "когда 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"..." где.' ..'is (1) no break, что эквивалентно(2) NOT [условие (ы) повышение break].

я думаю, что ключ в том, что 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 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!

С docs: операторы break и continue, а также предложения else на циклах

операторы цикла могут иметь предложение 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

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


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

предположим у нас есть функция

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 как сломанный, иначе оператор все равно будет напечатан. То есть :-

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

печати

Everything broken... doom is upon us

Я надеюсь, что он очищает путаницу вместо создания нового: -)


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")

он владеет, чтобы написать такие длинные заявления, как" предложение for завершено", поэтому они вводят"else".

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