Циклы в Питоне: for, range

Циклы в Python. Часть 2 – for, in, range

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

FOR, IN

Бывает такое, что нужно пройтись по всем элементам какой-то последовательности. Например, массива, строки или результатов запроса к внешнему источнику. Давайте попробуем пройтись по элементам массива и строки:

l = ["элемент1", "элемент2", "элемент3"]

for el in l:
    print(el)

s = "просто строка с буквами"
for c in s:
    print(c)

Видим, что для массива со строками мы получили всех «детей», это три строки. Для обычной строки дети, это буквы. Все последовательности в Питоне, это как контейнеры, в которых лежит что-то. Это что-то может тоже считаться контейнером, в котором лежит что-то.

Например, мы могли бы пробегать по каждому элементу массива, а потом по каждой букве полученного элемента. Вот так:

l = ["элемент1", "элемент2", "элемент3"]

for el in l:
    print("В массиве строка", el)

    for c in el:
        print("В строке символ", c)

Давайте я расскажу подробнее, что происходит:

В начале я объявляю массив, который содержит три строки.

l = ["элемент1", "элемент2", "элемент3"]

В качестве имени я выбрал латинскую букву l, что означает list (список). Вообще это плохой стиль и не повторяйте за мной – использовать одну букву в качестве названия объекта дурной тон, а так же легко перепутать латинскую l и цифру 1, ноль 0 и букву О, и т.п. Есть шрифты, которые заточены на выделение подобных различий, но гораздо лучше не творить фигни заранее.

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

Т.е. список ссылается на строки, строки состоят из букв. Всё это очень условно, и реализация работы с памятью меняется от версии к версии Питона, но цель сегодняшней статьи – циклы.

Итак, мы определили массив (он же список). Далее хотим пробежаться по его элементам. Вот строка:

for el in l:

В ней я сообщаю питону, что хотел бы пройтись по каждому элементу (for … in l), и в процессе работы цикла буду давать элементу имя el. Когда цикл начинает работу, он создает переменную el, в которой лежит первая строка списка. Я вывожу её на экран:

print("В массиве строка", el)

Умный питон понимает, что я хочу вывести текстовое представление переменной el (точнее, есть магические методы __str__ и __repr__, которые он вызывает у объекта el, но не забивайте пока голову), и в результате в своем print я вижу строку.

Когда сработает следующий for el in l:, в переменой el окажется уже адрес следующей строки, и она так же будет выведена на экран. И так три раза, т.к. у нас массив из трех элементов.

Но, каждый раз когда мы получаем строку, мы же по ней и пробегаемся. Вот в этой строке: for c in el:

Умный питон видит, что мы хотим пробежаться по каждому элементу строки, и дать ему имя c (от слова char, символ). У нас уже не for … in l, а for … in el – изменился объект, по которому мы итерируемся. Не список, а элемент списка.

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


Реклама


RANGE

А еще бывают такие моменты, когда последовательности нет, но нам нужно сделать что-то определенное кол-во раз. Например, посчитать сложные проценты. Мы это делали в первой статье цикла, теперь изучим вопрос подробнее.

Слово range позволяет сгенерировать диапазон значений.

r = range(1, 10)
print(list(r))

r = range(1, 10, 2)
print(list(r))

r = range(10, 1, -1)
print(list(r))

r = range(0, 100000000000000000000000000)
print(r)
for i in r:
    print("huge range", i)

 

 

Сначала мы просто определили диапазон от 1 до 10 (левая граница входит в диапазон, правая не входит), и получили значение [1, 2, 3, 4, 5, 6, 7, 8, 9]

Потом объявили диапазон от 1 до 10 с шагом 2 – и получили [1, 3, 5, 7, 9]

Потом объявили диапазон от 10 до 1 (обратный порядок) с шагом в -1 – и получили [10, 9, 8, 7, 6, 5, 4, 3, 2]

Заметьте, я для вывода использовал конструкцию list(r) – приводил range к списку. Дело в том, что range отчасти волшебный объект, он генератор. Он ничего не делает, пока вы к нему не обратитесь. То, что вы написали r = range(1, 10, 2), на самом деле не выделило память для 9 чисел, но когда вы скажете for … in r: он вычислит первое значение, потом второе, потом третье и т.п.

Обратите внимание на этот пример - r = range(0, 100000000000000000000000000) –у меня не хватит оперативной памяти (я даже не знаю у кого хватит), что бы выделить такой здоровый объем.

Если я напишу print(list(r)) после такого объявления, то генератор начнет выделять память и заполнять список, что бы вернуть его мне. Умрет и я ничего не добьюсь.

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

for i in r:
    print("huge range", i)

Реклама


Enumerate

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

l = ["элемент1", "элемент2", "элемент3"]

for pos, el in enumerate(l):
    print(pos, el)

Добавилось слово enumerate, и в for …. In теперь не одна переменная, а две.


Реклама


Заключение

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



Категория: Программирование
Последнее изменение:

Не забудьте рассказать друзьям об этой статье.
Чтобы поддержать ресурс Bablofil достаточно просто поделиться с друзьями этой статьей в социальных сетях. Каждый репост - это самая высокая оценка качества материала. Спасибо, что читаете этот блог.



Комментарии
Пожалуйста, авторизуйтесь, что бы оставить свой комментарий
Крипто-кошельки для помощи и благодарности проекту:

Bitcoin адрес проекта: [[address]]

Перевод на сумму [[value]] BTC получен. Спасибо!.
[[error]]

Ethereum адрес проекта: [[address]]