Расскажу, как устроены циклы в Питоне. Это супер важная часть, которая нужна почти в любом вашем коде, потому что чаще всего мы работаем со списками элементов и значений. Поэтому давайте разбираться, как они устроены и как их применять. Глянем сначала абстрактно, потом на примерах.
Циклы в программировании
Компьютер — максимально тупой аппарат, но при этом максимально быстрый по сравнению с человеком. По факту компьютер очень быстро перебирает варианты и ищет подходящий. В этом его сила. И сила эта реализуется через циклы — специальный инструмент, который позволяет пробегаться по значениям и что-то с ними делать.
Есть разные виды циклов, но чаще всего применяют циклы while и for. Я пользуюсь вторым циклом, потому что он для меня проще, полезнее и с куда меньшей вероятностью может зависнуть. Потому что цикл for проходится по выданному ему объекту, а когда объект заканчивается, то и цикл завершается. А цикл while выполняется до тех пор, пока не сработает какое-то условие. И так как не факт, что условие выполнится, то программа забивает всю память компьютера, и он зависает.
Так что буду рассказывать про цикл for. В этой статье разбираем концептуально и синтаксис, позже будут статьи, в которых уже будем применять циклы на рабочих задачах. Так что цель — разобраться, как оно работает, а уже практика будет позже.
Объекты для обработки циклом
В цикл можно подавать итерируемые объекты, по-русски — перебираемые. Итерируемые объекты состоят из элементов, которые можно перебирать по одному. Например, это списки, строки, словари и кортежи.
Список состоит из каких-то элементов вроде чисел или слов или других списков. Его содержимое можно перебирать. Строка — это набор букв, поэтому строку можно перебирать по буквам. Про словари мы будем говорить дальше в отдельном материале, но по сути это как бы два спаренных списка. Кортежи — это специальные списки, которые нельзя изменить после создания. С ними работать не будем, кортежи для депутатов, а мы люди простые.
Число, труба из Ревита или булевы значения (истина и ложь) — неитерируемые элементы, по ним нельзя запустить цикл, программа выдаст ошибку. Поэтому важно подавать в циклы итерируемые объекты. В нашей практике это будут чаще всего списки с элементами Ревита или значениями вроде текста или чисел. Всякие длины труб, имена систем и всё такое.
Цикл for получает список, перебирает каждый его элемент и выполняет с каждым элементом то, что мы его попросим. Когда элементы кончаются, то и цикл завершает работу. Один проход цикла называется итерацией. Если подаём список из пяти элементов, то будет пять итераций, если мы их не прервём раньше.
Чтобы визуализировать работу циклов, я иногда представляю себе коробку, в которой что-то лежит. Циклом я залезаю коробку и вытаскиваю её содержимое по одной штуке за раз. Вытащил — что-то сделал, вытащил следующее — что-то сделал. При этом в коробке могут быть и другие коробки. Тогда мне придётся залезать ещё и в эти коробки, чтобы вытащить из них каждую штуку. Это будет цикл в цикле, сон во сне, иголка в зайце, пейн ин эсс.
Синтаксис цикла for
Посмотрим на примере простого объекта — списка чисел. Именно список чисел, а не просто отдельное число, потому что число нельзя перебирать, а список — можно.
На языке Питона это будет выглядеть так, слева в Код Блоке создал список чисел от 0 до 5 и подал в Питон-нод:

for chislo in spisok_chisel:
# тут что-то делаем
# с элементами списка
После двоеточия делаем абзац, дальше — список команд, которые будут выполняться внутри цикла. Чтобы компьютер понимал, какие команды надо выполнять внутри данного цикла, то все последующие строки внутри цикла мы пишем с отступом. Отступы можно делать либо клавишей табуляции, если вы психически здоровый человек, либо с помощью четырёх пробелов, если вы больной ублюдок. Если один раз нажать Таб вам сложнее, чем четыре раза тыкать пробел, то вам надо к доктору.

Это довольно важно, потому что код, в котором где-то табы, где-то пробелы просто не запустится. Получите ошибку, показал её на скриншоте ниже. Я вам покажу, как проверять код, как быстро поменять пробелы на табы или обратно, но это будет позже, когда дойдём до работы с VS Code.

Кстати, в документации к Питону пишут, что предпочтительно использовать четыре пробела. Делайте выводы об этих ребятах, делайте выводы.
По-русски запись цикла можно прочитать так: для каждого значения в списке итерируемом объекте.
Здесь chislo — это переменная, которую объявил, чтобы в неё класть элементы списка. По сути я в эту переменную на каждой итерации цикла по очереди копирую значения из списка. Это не сам элемент списка, дальше увидим, почему.
Переменная нужна, потому что в программировании все данные должны храниться в памяти компьютера. Чтобы выделить место в памяти под что-то, мы создаём переменную. Список чисел уже создали, он уже занимает какое-то место в памяти.
Но мы не можем залезть в эту память и просто выцарапать оттуда кусок, в котором лежит элемент списка. Мы должны зарезервировать под этот элемент новое место в памяти, куда скопируем наш элемент, ведь из списка он никуда не делся. Делается это через объявление новой переменной.
for и in — это ключевые слова, с помощью for объявили цикл, с помощью in указали, в каком итерируемом объекте будем брать элементы. Ключевые слова нельзя использовать как переменные, мы не можем назвать переменную for, программа нас не поймёт.
spisok_chisel — наш список с числам, по которому проходимся циклом, по которому итерируемся.
Цикл не может ничего не делать, после двоеточия должна быть какая-то команда, иначе получим ошибку. Давайте для примера добавим единицу к каждому элементу списка и выведем результат через OUT. Это будет выглядеть вот так:

Как видим — ничего не поменялось. Наш исходный список не изменился, единица к каждому числу в нём не прибавилась.
Дело в том, что внутри цикла мы меняли значение переменной chislo, но ведь это совершенно отдельная переменная. Мы просто последовательно копировали в неё значения из списка, добавляли единицу, потом в эту же переменную копировали следующее значение, добавляли единицу и так далее.
Мы никуда не записывали результат наших действий, а действия производили не над самим списком, а над переменной, куда копировали числа из списка. Представьте, что взяли книгу, сделали копии страниц, а потом порвали их. Копии вы порвали, но сама-то книга осталась неизменной.
Запись новых значений в список
Чтобы записать результат преобразований в цикле, создадим новый список, куда будем складывать новые числа. Для этого нужно объявить новую переменную и записать в неё пустой список. Делается это очень просто: noviy_spisok = []
. Вот эти квадратные скобки означают список, а так как внутри скобок ничего нет, то список будет пустым в момент создания.
Чтобы добавить в список элемент, применяют метод append. Пока просто запомните, разбираться с этим будем в отдельной статье. Запись выглядит так: noviy_spisok.append(chislo)
Синтаксис такой: мы берём список, ставим точку, пишем имя метода, а в скобках указываем, что хотим засунуть в список. Метод будет закидывать элемент в конец списка.
Когда пишете имя переменной со списком, а потом ставите точку и пишете букву a на латинице, то редактор кода в Динамо сразу предлагает список методов, первый в списке — нужный нам метод append. Нажмите энтер и писать его целиком не придётся. Только заглавную А замените на строчную. Зачем они сделали с заглавных букв — непонятно.

Про методы будем говорить в другой статье.
Где создавать пустой список
При этом важно, где мы создадим этот пустой список. В данном случае у нас два варианта: перед циклом или внутри цикла. После цикла создавать бессмысленно, потому что программа «читается» сверху вниз, нельзя сначала что-то добавить в список, а только потом его создать.
Если создадим список перед циклом, то всё будет работать правильно. Алгоритм получается следующий:
- У нас есть исходный список и наш новый пустой. Они оба уже существуют. То есть нам есть, откуда брать и куда складывать элементы списка.
- Мы заходим циклом в исходный список. Берём первый элемент списка.
- Меняем элемент списка. Кладём его в новый пустой. Теперь новый список не пустой, в нём один объект.
- Запускается следующая итерация цикла: заходим во второй элемент исходного списка, меняем, складываем в новый список.
- Цикл завершается, когда мы зайдём в каждый элемент, изменим его и положим в новый список.
- На выходе у нас будет неизменный исходный список и новый список с изменёнными значениями
Если создадим список внутри цикла, то записать все новые значения в него не получится. Потому что алгоритм изменится:
- У нас есть исходный список. Новый пока не существует. Нам есть, откуда взять элементы, но положить пока некуда.
- Заходим циклом в исходный список. Создаём новый список. Берём первый элемент. Меняем его и кладём в новый список. Первая итерация завершена.
- Теперь мы циклом заходим во второй элемент. При этом мы опять создаём пустой список, старый при этом нигде не сохранился, так как мы в ту же переменную засунули новый пустой список. И снова кладём в него изменённый элемент.
- И так будет в каждой итерации цикла.
- Когда цикл завершится, мы получим на выходе неизменный исходный список и новый список с последним элементом исходного списка.
Всё потому, что мы по факту в каждой итерации заново генерировали пустой список. Мы не сохраняли результат всех действий после выполнения каждой итерации. Если в моём примере выше взять и вывести в OUT не исходный список чисел, а саму переменную chislo, то мы получим значение последнего элемента списка плюс один.

Поэтому нужно следить за тем, где создаёте списки. Обычно правило такое: пустой список нужно создавать на том же уровне, с какого берёте данные. Мы сейчас брали данные из списка, в котором нет вложенных списков, то есть брали данные с самого верхнего уровня. Поэтому и пустой список создаём на самом верхнем уровне — вне цикла и перед ним.
Рабочий код
Итак, код будет выглядеть вот так:

spisok_chisel = IN[0] # исходный список
noviy_spisok = [] # список для записи значений
for chislo in spisok_chisel:
chislo = chislo + 1
noviy_spisok.append(chislo)
OUT = noviy_spisok
Я создал новый список, а потом внутри цикла в него закидываю изменённое число. В итоге на выход подаю свой новый список и там всё в порядке, к каждому числу исходного добавилась единица.
В общих чертах вот так работает цикл.
На самом деле, мы можем изменять элементы внутри списка без создания нового, но об этом расскажу в статье про работу со списками в Питоне.
Склеивание нескольких списков в цикле
Супер полезная штука, когда нужно что-то сделать с двумя или более списками. Как правило, это списки равной длины, то есть у них одинаковое количество элементов.
Для примера возьмём два списка чисел. Задача — перемножить каждый элемент списка с его «соседом» из второго списка. То есть первый элемент первого списка умножить на первый элемент второго списка, далее второй из первого на второй из второго и так далее.

Для этого мы воспользуемся функцией zip. Мы как бы склеим два списка и в момент обработки циклом за одну итерацию мы будем брать последовательно элементы из всех склеенных списков. То есть функция как бы ставит две коробки с предметами рядом и за одну итерацию вытаскиваем элементы сразу из двух коробок.
Именно это нам и надо, чтобы за одну итерацию перемножить соответствующие элементы из списков.
Код будет выглядеть вот так:

spisok_chisel_1 = IN[0]
spisok_chisel_2 = IN[1]
umnozhenie = []
for num1, num2 in zip(spisok_chisel_1, spisok_chisel_2):
resultat = num1 * num2
umnozhenie.append(resultat)
OUT = umnozhenie
Что тут происходит. Создали два списка чисел в Динамо, подали в Питон-скрипт, присвоили две переменные spisok_chisel_1 и spisok_chisel_2. Дальше создал новый пустой список для записи результатов умножения.
И теперь цикл. В нём я пишу ключевое слово for, но затем создаю две переменные через запятую, потому что у меня два списка, и из каждого надо взять по одному элементу. Их надо записать в память компьютера, поэтому нужны две переменные. Назвал их num1 и num2.
После этого склеиваю списки функцией zip. Как она работает под капотом, неважно, главное, что делает то, что нужно.
Внутри цикла создаю переменную resultat, чтобы в ней перемножить два числа. И добавляю этот результат в новый список. Вывожу его на OUT и получаю нужный результат.
Функция zip тут очень облегчает работу. Без неё я бы делал цикл в цикле, брал бы индексы элементов, чтобы найти соответствующий, в общем, это была бы морока. А тут всё так просто происходит. Про работу с индексами списков тоже поговорим, то в статье о списках.
Вот так на базовом уровне работают циклы. В реальной работе вы будете итерироваться по разным спискам: и элементы Ревита, и числа, и текст, и вложенные списки. Сперва будет тяжеловато, особенно когда речь про вложенные списки, но со временем придёт понимание. Раскладывайте в уме списки на «коробки», можете в голове крутить итерации и писать результат на бумажку, а потом сравнивайте с тем, что выдаёт Питон, ищите ошибки. Это всё довольно абстрактно, поэтому понимание приходит не сразу.



