Циклы Python "while" (бесконечная итерация)

Оглавление

Итерация означает выполнение одного и того же блока кода снова и снова, потенциально много раз. Структура программирования, реализующая итерацию, называется loop.

В программировании существует два типа итерации - неопределенная и определенная:

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

  • При бесконечной итерации количество выполнений обозначенного блока задается явно в момент запуска цикла.

В этом уроке вы узнаете:

  • Узнайте о цикле while, управляющей структуре Python, используемой для неограниченной итерации
  • Узнайте, как преждевременно выйти из цикла или итерации цикла
  • Исследуйте бесконечные циклы

Когда вы закончите, вы должны хорошо понимать, как использовать неопределенную итерацию в Python.

Цикл while

Давайте посмотрим, как оператор while в Python используется для построения циклов. Мы начнем с простого и будем приукрашивать по ходу дела.

Формат простейшего цикла while показан ниже:

while <expr>:
    <statement(s)>

<statement(s)> представляет собой блок для повторного выполнения, часто называемый телом цикла. Он обозначается с отступом, как и в операторе if.

Помните: Все управляющие структуры в Python используют отступы для определения блоков. См. обсуждение группировки операторов в предыдущем уроке для ознакомления.

Управляющее выражение, <expr>, обычно включает одну или несколько переменных, которые инициализируются перед началом цикла, а затем изменяются в теле цикла.

Когда встречается цикл while, <expr> сначала оценивается в булевом контексте. Если оно истинно, то выполняется тело цикла. Затем снова проверяется <expr>, и если оно по-прежнему истинно, тело цикла выполняется снова. Так продолжается до тех пор, пока <expr> не станет ложным, после чего выполнение программы переходит к первому оператору за пределами тела цикла.

Рассмотрите этот цикл:

 1 >>> n = 5
 2 >>> while n > 0:
 3 ...     n -= 1
 4 ...     print(n)
 5 ...
 6 4
 7 3
 8 2
 9 1
10 0

Вот что происходит в этом примере:

  • n изначально является 5. Выражение в заголовке оператора while в строке 2 равно n > 0, что является истиной, поэтому тело цикла выполняется. Внутри тела цикла в строке 3 значение n уменьшается на 1 до 4, а затем выводится на печать.

  • После завершения тела цикла выполнение программы возвращается в начало цикла в строке 2, и выражение оценивается снова. Оно все еще истинно, поэтому тело цикла выполняется снова, и печатается 3.

  • Так продолжается до тех пор, пока n не станет 0. В этот момент, когда выражение проверяется, оно оказывается ложным, и цикл завершается. Выполнение возобновится с первого оператора, следующего за телом цикла, но в данном случае его нет.

Обратите внимание, что управляющее выражение цикла while проверяется первым, прежде чем произойдет все остальное. Если оно равно false, тело цикла вообще не будет выполнено:

>>> n = 0
>>> while n > 0:
...     n -= 1
...     print(n)
...

В приведенном выше примере, когда встречается цикл, n становится 0. Управляющее выражение n > 0 уже равно false, поэтому тело цикла никогда не выполнится.

Вот еще один цикл while, в котором используется не числовое сравнение, а список:

>>> a = ['foo', 'bar', 'baz']
>>> while a:
...     print(a.pop(-1))
...
baz
bar
foo

Когда список оценивается в булевом контексте, он является истинным, если в нем есть элементы, и ложным, если он пуст. В данном примере a истинен до тех пор, пока в нем есть элементы. Когда все элементы будут удалены методом .pop() и список станет пустым, a станет ложным, и цикл завершится.

Операторы break и continue

В каждом примере, который вы видели до сих пор, на каждой итерации выполняется все тело цикла while. Python предоставляет два ключевых слова, которые преждевременно завершают итерацию цикла:

  • Оператор Python break немедленно завершает цикл полностью. Программа переходит к первому оператору, следующему за телом цикла.

  • Оператор Python continue немедленно завершает текущую итерацию цикла. Выполнение переходит в начало цикла, и управляющее выражение переоценивается, чтобы определить, будет ли цикл выполняться снова или завершится.

Различие между break и continue продемонстрировано на следующей диаграмме:

Python while loops: break and continue statements

Вот файл сценария под названием break.py, который демонстрирует оператор break:

 1 n = 5
 2 while n > 0:
 3     n -= 1
 4     if n == 2:
 5         break
 6     print(n)
 7 print('Loop ended.')

Запуск break.py из интерпретатора командной строки приводит к следующему результату:

Командная строка Windows

C:\Users\john\Documents>python break.py
4
3
Loop ended.

Когда n становится 2, выполняется оператор break. Цикл полностью завершается, и выполнение программы переходит к оператору print() в строке 7.

Примечание: Если вы программируете на C, C++, Java или JavaScript, то вам может быть интересно, где в Python находится цикл do-while. Плохая новость заключается в том, что в Python нет конструкции do-while. Но хорошая новость заключается в том, что вы можете использовать цикл while с оператором break, чтобы имитировать его.

Следующий сценарий, continue.py, идентичен, за исключением оператора continue на месте break:

 1 n = 5
 2 while n > 0:
 3     n -= 1
 4     if n == 2:
 5         continue
 6     print(n)
 7 print('Loop ended.')

Вывод continue.py выглядит следующим образом:

Командная строка Windows

C:\Users\john\Documents>python continue.py
4
3
1
0
Loop ended.

На этот раз, когда n становится 2, оператор continue приводит к завершению этой итерации. Таким образом, 2 не выводится. Выполнение возвращается в начало цикла, условие переоценивается, и оно по-прежнему истинно. Цикл возобновляется и завершается, когда n становится 0, как и раньше.

Оператор else

Python допускает необязательное предложение else в конце цикла while. Это уникальная особенность Python, которой нет в большинстве других языков программирования. Синтаксис показан ниже:

while <expr>:
    <statement(s)>
else:
    <additional_statement(s)>

Указанный в пункте else <additional_statement(s)> будет выполнен, когда цикл while завершится.

thought balloon

Сейчас вы, возможно, подумаете: "Как это может быть полезно?". Вы можете сделать то же самое, поместив эти утверждения сразу после цикла while, без else:

while <expr>:
    <statement(s)>
<additional_statement(s)>

В чем разница?

В последнем случае, без условия else, <additional_statement(s)> будет выполнен после завершения цикла while, независимо ни от чего.

Когда <additional_statement(s)> помещается в предложение else, он будет выполнен только в том случае, если цикл завершится "по исчерпанию", то есть если цикл будет итерироваться до тех пор, пока управляющее условие не станет ложным. Если цикл завершается оператором break, то предложение else не будет выполнено.

Рассмотрим следующий пример:

>>> n = 5
>>> while n > 0:
...     n -= 1
...     print(n)
... else:
...     print('Loop done.')
...
4
3
2
1
0
Loop done.

В этом случае цикл повторялся до тех пор, пока условие не было исчерпано: n стало 0, поэтому n > 0 стало ложным. Поскольку цикл, так сказать, прожил свою естественную жизнь, был выполнен пункт else. Теперь обратите внимание на разницу здесь:

>>> n = 5
>>> while n > 0:
...     n -= 1
...     print(n)
...     if n == 2:
...         break
... else:
...     print('Loop done.')
...
4
3
2

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

Может показаться, что значение слова else не совсем подходит для цикла while, как и для оператора if.

Одна из следующих интерпретаций может помочь сделать это более интуитивно понятным:

  • Представьте себе заголовок цикла (while n > 0) как if оператор (if n > 0), который выполняется снова и снова, а пункт else выполняется, когда условие становится ложным.

  • Думайте о else так, как если бы это было nobreak, в том смысле, что блок, который следует за ним, будет выполнен, если не было break.

Если вы не находите ни одну из этих интерпретаций полезной, то смело игнорируйте их.

Когда может быть полезным предложение else в цикле while? Одна из распространенных ситуаций - поиск определенного элемента в списке. Вы можете использовать break для выхода из цикла, если элемент найден, а предложение else может содержать код, который будет выполнен, если элемент не будет найден:

>>> a = ['foo', 'bar', 'baz', 'qux']
>>> s = 'corge'

>>> i = 0
>>> while i < len(a):
...     if a[i] == s:
...         # Processing for item found
...         break
...     i += 1
... else:
...     # Processing for item not found
...     print(s, 'not found in list.')
...
corge not found in list.

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

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

Во-вторых, Python предоставляет встроенные способы поиска элемента в списке. Вы можете использовать оператор in:

>>> if s in a:
...     print(s, 'found in list.')
... else:
...     print(s, 'not found in list.')
...
corge not found in list.

Также подойдет метод list.index(). Этот метод вызывает исключение ValueError, если элемент не найден в списке, поэтому для его использования необходимо понимать обработку исключений. В Python для обработки исключения используется оператор try. Пример приведен ниже:

>>> try:
...     print(a.index('corge'))
... except ValueError:
...     print(s, 'not found in list.')
...
corge not found in list.

Вы узнаете об обработке исключений позже в этой серии.

Клаузула else с циклом while - это немного странное явление, которое не часто встречается. Но не отказывайтесь от него, если вы найдете ситуацию, в которой, по вашему мнению, он добавит ясности вашему коду!

Бесконечные циклы

Предположим, что вы написали цикл while, который теоретически никогда не заканчивается. Звучит странно, правда?

Рассмотрим этот пример:

>>> while True:
...     print('foo')
...
foo
foo
foo
  .
  .
  .
foo
foo
foo
Traceback (most recent call last):
  File "<pyshell#2>", line 2, in <module>
    print('foo')
KeyboardInterrupt

This code was terminated by Ctrl+C, which generates an interrupt from the keyboard. В противном случае он продолжался бы бесконечно. Многие строки вывода foo были удалены и заменены вертикальным многоточием в показанном выводе.

Очевидно, что никогда True не будет ложным, иначе у всех нас будут большие проблемы. Таким образом, while True: инициирует бесконечный цикл, который теоретически будет выполняться вечно.

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

Более прозаично, помните, что из циклов можно выйти с помощью оператора break. Возможно, будет проще завершить цикл, основываясь на условиях, распознанных в теле цикла, а не на условии, оцененном в верхней части.

Вот еще один вариант цикла, показанного выше, который последовательно удаляет элементы из списка с помощью .pop(), пока он не станет пустым:

>>> a = ['foo', 'bar', 'baz']
>>> while True:
...     if not a:
...         break
...     print(a.pop(-1))
...
baz
bar
foo

Когда a становится пустым, not a становится истиной, и оператор break выходит из цикла.

Вы также можете указать несколько операторов break в цикле:

while True:
    if <expr1>:  # One condition for loop termination
        break
    ...
    if <expr2>:  # Another termination condition
        break
    ...
    if <expr3>:  # Yet another
        break

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

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

Вложенные while циклы

В общем случае управляющие структуры Python могут быть вложены друг в друга. Например, условные операторы if/elif/else могут быть вложены друг в друга:

if age < 18:
    if gender == 'M':
        print('son')
    else:
        print('daughter')
elif age >= 18 and age < 65:
    if gender == 'M':
        print('father')
    else:
        print('mother')
else:
    if gender == 'M':
        print('grandfather')
    else:
        print('grandmother')

Аналогично, цикл while может быть заключен внутри другого цикла while, как показано здесь:

>>> a = ['foo', 'bar']
>>> while len(a):
...     print(a.pop(0))
...     b = ['baz', 'qux']
...     while len(b):
...         print('>', b.pop(0))
...
foo
> baz
> qux
bar
> baz
> qux

Утверждение break или continue, встречающееся во вложенных циклах, применяется к ближайшему вложенному циклу:

while <expr1>:
    statement
    statement

    while <expr2>:
        statement
        statement
        break  # Applies to while <expr2>: loop

    break  # Applies to while <expr1>: loop

Кроме того, циклы while могут быть вложены внутрь операторов if/elif/else, и наоборот:

if <expr>:
    statement
    while <expr>:
        statement
        statement
else:
    while <expr>:
        statement
        statement
    statement
while <expr>:
    if <expr>:
        statement
    elif <expr>:
        statement
    else:
        statement

    if <expr>:
        statement

На самом деле, все управляющие структуры Python можно смешивать друг с другом в любой степени, которая вам необходима. Так и должно быть. Представьте, как было бы неприятно, если бы существовали неожиданные ограничения вроде "Цикл while не может содержаться внутри оператора if" или "Циклы while могут быть вложены друг в друга не более чем на четыре глубины". Вам будет очень трудно запомнить их все.

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

Однострочные while циклы

Как и в случае с оператором if, цикл while может быть задан в одной строке. Если в блоке, составляющем тело цикла, содержится несколько операторов, их можно разделить точками с запятой (;):

>>> n = 5
>>> while n > 0: n -= 1; print(n)

4
3
2
1
0

Однако это работает только с простыми утверждениями. Вы не можете объединить два составных оператора в одной строке. Таким образом, вы можете указать цикл while в одной строке, как указано выше, и написать оператор if в одной строке:

>>> if True: print('foo')

foo

Но вы не можете сделать этого:

>>> while n > 0: n -= 1; if True: print('foo')
SyntaxError: invalid syntax

Помните, что PEP 8 не рекомендует использовать несколько утверждений в одной строке. Поэтому вам, вероятно, не стоит делать все это очень часто.

Заключение

В этом уроке вы узнали о бесконечной итерации с помощью цикла Python while. Теперь вы можете:

  • Построение основных и сложных циклов while
  • Прерывайте выполнение циклов с помощью break и continue
  • Используйте предложение else с циклом while
  • Работайте с бесконечными циклами

Теперь у вас должно быть хорошее представление о том, как выполнять кусок кода повторно.

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

Вернуться на верх