Как я могу понять предложение "else" петель Python?



многие программисты Python, вероятно, не знают, что синтаксис while петли и for петли включает в себя дополнительный else: статья:



for val in iterable:
do_something(val)
else:
clean_up()


тело else предложение является хорошим местом для определенных видов действий очистки и выполняется при нормальном завершении цикла: т. е., выход из цикла с return или break переход else предложение; выход после a continue выполняет его. Я знаю это только потому, что я просто посмотрел (пока опять же), потому что я никогда не могу вспомнить , когда the else пункт выполняется.



всегда? На "провал" цикла, как следует из названия? По обычному увольнению? Даже если цикл завершается с return? Я никогда не могу быть полностью уверен, не посмотрев его.



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



я уверен, что было много дискуссий об этом, и я могу себе представить, что выбор был сделан для согласованности с try заявление else: предложение (которое я также должен посмотреть), и с целью не добавлять в список зарезервированных Python слова. Возможно, причины выбора else прояснит его функцию и сделает ее более запоминающейся, но я после подключения имени к функции, а не после исторического объяснения как такового.



ответы на этот вопрос, который мой вопрос был кратко закрыт как дубликат, содержат много интересной предыстории. Мой вопрос имеет другой фокус (как подключить конкретную семантику else С выбором ключевого слова), но я чувствую, что должна быть ссылка на этот вопрос где-то.

691   14  

14 ответов:

(это вдохновлено ответом @Mark Tolonen.)

An if оператор запускает его else предложение, если его условие оценивается как false. Тож, а while цикл выполняется предложение else, если condition имеет значение false.

это правило соответствует описанному вами поведению:

  • в обычном исполнении цикл while повторяется до тех пор, пока условие не будет оценено как false, и поэтому, естественно, выход из цикла запускает else пункт.
  • при выполнении break оператор, вы выходите из цикла без оценки условия, поэтому условие не может быть оценено как false, и вы никогда не запускаете предложение else.
  • при выполнении continue оператор, вы снова оцениваете условие и делаете именно то, что обычно делаете в начале итерации цикла. Итак, если условие истинно, вы продолжаете цикл, но если оно ложно, вы запускаете предложение else.
  • другие методы выход из цикла, например return, не оценивайте условие и поэтому не запускайте предложение else.

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

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

право в этом контексте означает нет exception, нет break, нет return. Любое заявление, которое захватывает контроль от for вызывает else блок, который нужно обойти.


общий случай использования при поиске элемента в Ан iterable, для которого поиск либо отменяется, когда элемент найден, либо "not found" флаг поднимается / печатается через следующее else блок:

for items in basket:
    if isinstance(item, Egg):
        break
else:
    print("No eggs in basket")  

A continue не захватывает управление от for, поэтому управление будет переходить к else после for исчерпан.

когда if выполнить else? Когда условие ложно. Это точно то же самое для while/else. Так что вы можете думать о while/else Как только if который продолжает выполнять свое истинное состояние, пока не оценит false. А break ничего не меняет. Он просто прыгает из содержащего цикла без оценки. Элемент else выполняется только если оценка the if/while условие ложно.

The for is подобное, за исключением его ложного условия, исчерпывает его итератор.

continue и break не выполнять else. Это не их функция. Элемент break выход из содержащего цикла. Элемент continue возвращается в верхнюю часть содержащего цикла, где вычисляется условие цикла. Это акт оценки if/while false (или for не имеет больше элементов), который выполняет else и никак иначе.

вот что это по существу означает:

for/while ...:
    if ...:
        break
if there was a break:
    pass
else:
    ...

это более приятный способ написания этого общего шаблона:

found = False
for/while ...:
    if ...:
        found = True
        break
if not found:
    ...

The else предложение не будет выполнено, если есть return, потому что return оставляет функцию, как это и должно быть. Единственное исключение из того, о чем вы можете думать, это finally, чья цель состоит в том, чтобы быть уверенным, что он всегда выполняется.

continue не имеет ничего особенного к этому вопросу. Это вызывает ток итерация цикла до конца, которая может привести к завершению всего цикла, и, очевидно, в этом случае цикл не был закончен break.

try/else аналогично:

try:
    ...
except:
    ...
if there was an exception:
    pass
else:
    ...

если вы думаете о своих петлях как о структуре, подобной этой (несколько псевдокод):

loop:
if condition then

   ... //execute body
   goto loop
else
   ...

это может иметь немного больше смысла. Цикл по существу является просто if оператор, который повторяется до тех пор, пока условие не будет false. И это очень важный момент. Цикл проверяет свое состояние и видит, что это false, таким образом, выполняет else (так же, как нормальный if/else), а затем цикл делается.

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

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

мой попался момент с петлей else предложение было, когда я смотрел разговор по Компания Raymond Hettinger, который рассказал историю о том, как он думал, его надо было назвать nobreak. Взгляните на следующий код, как вы думаете он будет делать?

for i in range(10):
    if test(i):
        break
    # ... work with i
nobreak:
    print('Loop completed')

что бы вы предположили, что это делает? Ну, та часть, которая говорит nobreak будет выполняться только если a break заявление не попало в петлю.

обычно я склонен думать о структуре цикла, как это:

for item in my_sequence:
    if logic(item):
        do_something(item)
        break

чтобы быть очень похожим на переменное число if/elif отчетность:

if logic(my_seq[0]):
    do_something(my_seq[0])
elif logic(my_seq[1]):
    do_something(my_seq[1])
elif logic(my_seq[2]):
    do_something(my_seq[2])
....
elif logic(my_seq[-1]):
    do_something(my_seq[-1])

в этом случае else оператор на цикле for работает точно так же, как else заявление по цепочке elifs, он выполняется только в том случае, если ни одно из условий до его оценки не соответствует True. (или прервать выполнение с помощью return или исключение) если мой цикл не соответствует этой спецификации, как правило, я выбираю отказаться от используя for: else именно по этой причине вы разместили этот вопрос: он не интуитивно понятен.

другие уже объяснили механику while/for...else и Python 3 ссылка на язык есть авторитетное определение (см. пока и на), но вот моя личная мнемоника, FWIW. Я думаю, что ключом для меня было разбить это на две части: один для понимания смысла else по отношению к условному циклу и один для понимания управления циклом.

я считаю, что это проще всего начать понимание while...else:

while у вас есть больше деталей, делать вещи, else Если вы закончите, сделайте это

The for...else мнемоника в основном то же самое:

for каждый пункт, делать вещи, но else Если вы закончите, сделайте это

в обоих случаях else часть достигается только тогда, когда больше нет элементов для обработки, и последний элемент был обработан в a регулярным образом (т. е. нет break или return). А continue просто возвращается и видит, если есть еще какие-либо элементы. Моя мнемоника для этих правил применима к обоим while и for:

, когда breaking или returnИнг, нет ничего else делать
и когда я говорю continue, это "цикл назад, чтобы начать" для вас

- С "цикл назад, чтобы начать" означает, очевидно, начало цикла, где мы проверяем, есть ли есть ли еще элементы в iterable, так как else обеспокоен, continue на самом деле не играет никакой роли вообще.

на разработка на основе тестов (TDD), при использовании Предпосылка Приоритета Преобразования парадигма, вы рассматриваете циклы как обобщение условных операторов.

этот подход хорошо сочетается с этим синтаксисом, если рассматривать только простые if/else (не elif) заявления:

if cond:
    # 1
else:
    # 2

обобщается на:

while cond:  # <-- generalization
    # 1
else:
    # 2

красиво.

на других языках TDD переходит от одного случая к случаям с коллекции требуют большего рефакторинга.


вот пример 8thlight блог:

в связанной статье в блоге 8thlight рассматривается слово Wrap kata: добавление разрывов строк в строки (the s переменная в приведенных ниже фрагментах), чтобы они соответствовали заданной ширине (length переменная в приведенных ниже фрагментах). В какой-то момент реализация выглядит следующим образом (Java):

String result = "";
if (s.length() > length) {
    result = s.substring(0, length) + "\n" + s.substring(length);
} else {
    result = s;
}
return result;

и следующий тест, что в настоящее время терпит неудачу это:

@Test
public void WordLongerThanTwiceLengthShouldBreakTwice() throws Exception {
    assertThat(wrap("verylongword", 4), is("very\nlong\nword"));
    }

Итак, у нас есть код, который работает условно: при выполнении определенного условия добавляется разрыв строки. Мы хотим улучшить код для обработки нескольких разрывов строк. Решение, представленное в статье, предлагает применить (Если->а) преобразование, однако автор делает комментарий, что:

в то время как петли не могут иметь else предложения, поэтому нам нужно устранить else путь, делая меньше в if путь. Опять же, это рефакторинг.

что заставляет делать больше изменений в коде в контексте одного неудачного теста:

String result = "";
while (s.length() > length) {
    result += s.substring(0, length) + "\n";
    s = s.substring(length);
}
result += s;

в TDD мы хотим написать как можно меньше кода, чтобы тесты проходили. Благодаря синтаксису Python возможно следующее преобразование:

от:

result = ""
if len(s) > length:
    result = s[0:length] + "\n"
    s = s[length:]
else:
    result += s

to:

result = ""
while len(s) > length:
    result += s[0:length] + "\n"
    s = s[length:]
else:
    result += s

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

если вы break или return или raise вы не повторяете за конец цикла, вы останавливаетесь immeadiately, и таким образом else: блок не будет работать. Если вы continue вы все еще повторяете конец цикла, так как continue просто переходит к следующей итерации. Это не останавливает цикл.

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

но на самом деле, мое ментальное отображение-это просто "структурированная" версия шаблона C/C++ pattern:

  for (...) {
    ...
    if (test) { goto done; }
    ...
  }
  ...
done:
  ...

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

(я помещаю "структурированный" в кавычки страха, потому что разница заключается не в том, структурирован ли код или неструктурирован, а просто в том, есть ли ключевые слова и грамматика, посвященные конкретной структуре)

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

другие ключевые слова, которые вы упоминаете вырваться из цикла (выход ненормально) в то время как continue нет, он просто пропускает оставшуюся часть блока кода внутри цикла. Тот факт, что он может предшествовать завершению цикла, является случайным: завершение фактически выполняется обычным способом путем оценки условного выражения цикла.

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

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

позвольте мне продемонстрировать это на человеческом языке.

for каждый человек в группе подозреваемых if любой преступник break следствие. else отчет неудача.


else едва ли полезно, если бы не было break на for цикл в любом случае.

# tested in Python 3.6.4
def buy_fruit(fruits):
    '''I translate the 'else' below into 'if no break' from for loop '''
    for fruit in fruits:
        if 'rotten' in fruit:
            print(f'do not want to buy {fruit}')
            break
    else:  #if no break
        print(f'ready to buy {fruits}')


if __name__ == '__main__':
    a_bag_of_apples = ['golden delicious', 'honeycrisp', 'rotten mcintosh']
    b_bag_of_apples = ['granny smith', 'red delicious', 'honeycrisp', 'gala', 'fuji']
    buy_fruit(a_bag_of_apples)
    buy_fruit(b_bag_of_apples)

'''
do not want to buy rotten mcintosh
ready to buy ['granny smith', 'red delicious', 'honeycrisp', 'gala', 'fuji']
'''

Comments

    Ничего не найдено.