Почему 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."
чтобы сделать его простым, вы можете думать об этом;
- если он встречает на
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
заявления, все из которых эквивалентны, являются:
-
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"..." где.' ..'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