Ранее мы рассматривали цикл while
. Эта конструкция предназначена для повторения некоего набора действий — всё, что выходит за рамки "бездумного" повторения, как правило, требует дополнительных средств для хранения состояния. Пример: счётчик, который мы изменяем в цикле. И при работе с коллекциями нам нужно как-то выбирать, с каким элементом мы работаем в текущей итерации. Так что же, использовать переменную-счётчик каждый раз? Любой программист всегда стремится автоматизировать рутинную работу, и авторы языков — не исключение. Поэтому в Python для работы с коллекциями существует другой вид цикла — цикл for
.
Стоит сразу отметить, что этот цикл не похож на циклы с тем же названием в других языках программирования. Во многих языках этот цикл всего лишь дополняет условие завершения цикла переменной-счётчиком. Python в стремлении сделать удобно пошёл дальше, поэтому в этом языке цикл for
сразу перебирает элементы входной коллекции, и думать об индексе чаще всего вообще не нужно.
Цикл for
устроен очень просто:
for element in collection:
print(element) # this is body of cycle
Заметьте, в простейшем случае у цикла даже нет явного условия завершения: цикл просто останавливается, когда в коллекции заканчиваются элементы!
Пример выше сработает для кортежей и списков — в этом случае будут выведены все элементы. А если проитерировать (так называют обход коллекции — позже вы узнаете, почему) строку, то переменная цикла (в коде выше это element
) будет поочерёдно содержать все символы строки. Пример:
for c in 'Hello!':
print(c)
# => H
# => e
# => l
# => l
# => o
# => !
Но что же делать, если нам нужно не просто получить элементы списка один за другим, но и изменить эти элементы? Ведь для этого нам понадобится индекс каждого элемента! На этот случай в Python есть удобная функция enumerate
("пронумеровать"). Эта функция снабжает каждый элемент индексом, складывая каждый индекс вместе с элементом в кортеж. Кортежи эти, как правило, прямо в первой строке цикла и распаковывают:
items = ['foo', 'bar', 'baz']
for (index, elem) in enumerate(items):
items[index] = elem + '!'
print(items) # => ['foo!', 'bar!', 'baz!']
В этом цикле мы заменили каждый элемент оригинальным значением, дополненным строкой '!'
. Этот код можно было написать и несколько иначе:
items = ['foo', 'bar', 'baz']
for (index, _) in enumerate(items):
items[index] += '!'
print(items) # => ['foo!', 'bar!', 'baz!']
В этот раз мы вообще не используем сами элементы — только их индексы. Поэтому вместо переменной цикла, в которую распаковываются элементы, у нас стоит прочерк. Это не какая-то особая переменная, а всего лишь соглашение: в Python часто незначимые в данном контексте вещи "спихивают" в переменную _
.
Заметьте: хоть в последнем примере речь и шла об индексах, но мы всё равно не использовали длину коллекции — enumerate
тоже знает, где остановиться (в конце исходной коллекции).
break
и continue
.Иногда не нужно доходить до конца коллекции. Пример: поиск элемента, удовлетворяющего некоему условию. Как только мы нашли первый подходящий элемент, нам неплохо бы сэкономить ресурсы и завершить цикл. Такой ранний выход из цикла делается с помощью команды break
. Вот цикл поиска первого положительного числа:
items = [-2, 0, -10, 3, 5, -1]
for item in items:
if item > 0:
break
print(item) # => 3
Как вы могли заметить, переменная цикла оказалась доступна и после его завершения. Однако если коллекция окажется пустой, то переменная не будет определена — имейте это в виду!
Этот код, кажется, работает как надо. Однако если в списке не встретится ни одного положительного числа, то в переменной item
окажется просто последний элемент списка! Как же понять, что мы ничего не нашли? На помощь приходит else
— да, в Python у цикла for
тоже есть такая ветка! В цикле else
выполняется, если цикл так и не прервался с помощью break
. Для алгоритмов поиска — идеальный вариант! Перепишем наш пример с применением else
:
items = [-2, 0, -10, -1]
for item in items:
if item > 0:
break
else:
item = None
print(item) # => None
Победа!
Теперь представим ситуацию, что мы в процессе выполнения тела цикла поняли, что остаток тела выполнять незачем и можно сразу перейти к следующей итерации. Для перехода к следующей итерации предназначена команда continue
. Её использование продемонстрирует следующий пример: мы читаем строки, содержащие строчки кода, но нам не нужно обрабатывать код тех строчек, которые начинаются с символа #
. Вот так будет выглядеть код:
lines_of_code = [
'# begin of example',
'echo 123',
'cd foo',
'# end']
for line in lines_of_code:
if line[:1] == '#':
continue
# here we process a code
print(line)
# => echo 123
# => cd foo
Конечно же, мы могли бы обойтись условной конструкцией. Однако в этом случае код, обрабатывающий нужные строки, был бы вложен глубже. А нам нужно стремиться держать вложенность кода в разумных пределах, иначе код очень быстро станет очень сложным для прочтения.
break
, continue
, else
и цикл while
Да, и ветка else
, и команды break
и continue
— доступны и для цикла while
! Вот комплексный пример, демонстрирующий все эти возможности:
tries = 3
while tries:
print('>>> ', end='')
command = input()
if not command:
continue
if command in ('echo', 'cd', 'help'):
break
print('Unknown command!')
tries -= 1
else:
print('Too many bad tries!')
command = None
Этот код просит пользователя ввести одну из команд, игнорирует пустой ввод, ограничивает кол-во попыток ввода. Подумайте, какая часть тела цикла за что отвечает.
for
и изменяемые коллекцииХочу вас предостеречь от изменения состава списка во время обхода его же в цикле for
. Если вы будете удалять элементы из списка, по которому проходитесь — или даже всего лишь добавлять новые элементы в конец — результат может быть неожиданным, вплоть до завершения программы с ошибкой! Лучше наполнять новый список в процессе обхода старого.
Если же вы хотите обязательно изменить состав исходного списка (объекта по ссылке), то либо обходите в цикле копию списка
for x in original_list[:]:
original_list.pop(0) # и т.п.
либо создайте временный список, а потом очистите исходный и добавьте элементы из временного
new_list = []
for x in original_list:
...
original_list[:] = [] # удаляем старое содержимое
original_list.extend(new_list)
Естественно можно скомбинировать эти два варианта и сначала сделать копию, потом очистить оригинал, и затем уже в цикле обхода копии добавлять новые элементы.
Вам ответят команда поддержки Хекслета или другие студенты.
Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.
Загляните в раздел «Обсуждение»:
Статья «Ловушки обучения»
Вебинар «Как самостоятельно учиться»
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.
Наши выпускники работают в компаниях:
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Зарегистрируйтесь или войдите в свой аккаунт