Python. Циклы
Время чтения: 9 минут
Цикл (loop) – это механизм, позволяющий описать выполнение множества одинаковых действий.
Есть две разновидности циклов:
- for
- while
Цикл for
Вот пример определения цикла for:
some_list = [1, 2, 3, 4, 5, 0]
# Читается так: для каждого значения value из some_list выполнить
for value in some_list:
# value это временная переменная, которая создаётся при входе в цикл
# Ты можешь назвать её как угодно, не обязательно value -- например, если бы список назывался students, то я бы назвал переменную student
# В эту переменную по очереди забираются значения из списка some_list
print(value)
# 1
# 2
# 3
# 4
# 5
# 0
Вместо some_list может быть любой контейнер – для списка и кортежа цикл будет проходить по значениям, а для словаря цикл будет проходить по всем ключам:
some_dict = {
1: 2,
3: 4,
5: 6
}
for key in some_dict:
print(key)
# 1
# 3
# 5
Чтобы пройти по значениям словаря, надо у словаря вызвать функцию values() (мы её упоминали в статье про контейнеры):
some_dict = {
1: 2,
3: 4,
5: 6
}
for value in some_dict.values():
print(value)
# 2
# 4
# 6
Также можно пройтись сразу по парам ключ-значение через функцию items() (её тоже упомянали):
some_dict = {
1: (1, 2),
3: (3, 4),
5: (5, 6)
}
for key, value in some_dict.items():
print(key, value)
# 1 (1, 2)
# 3 (3, 4)
# 5 (5, 6)
Обрати внимание, что мы получаем сразу две переменных – key и value. Помнишь, я говорил что items() возвращает список, содержащий кортежи из двух элементов – ключа и значения? То, что здесь происходит, называется распаковкой.
Распаковка
Распаковка – это разложение контейнера на составляющие элементы. Пример:
some_tuple = (1, 2, 3)
# Мы можем распаковать этот кортеж в три переменных:
value_1, value_2, value_3 = some_tuple
То есть, чтобы распаковать контейнер мы объявляем столько же переменных, сколько находится значений в контейнере, и присваиваим им сам контейнер. Python увидит что мы написали сразу несколько переменных, и поймёт что мы хотим распаковать контейнер.
Если мы объявим больше или меньше переменных, чем находится в контейнере, то получим ошибку:
some_tuple = (1, 2, 3)
value_1, value_2 = some_tuple
Traceback (most recent call last):
File "/home/keereel/my-blog/content/python/test.py", line 2, in <module>
value_1, value_2 = some_tuple
ValueError: too many values to unpack (expected 2)
Распаковка довольно часто используется в циклах и вообще в коде. Посмотри как код выглядит без распаковки:
some_dict = {
1: (1, 2, 3),
2: (3, 4, 5),
3: (5, 6, 7)
for value in some_dict.values():
print(value[0], value[1], value[2])
# 1 2 3
# 3 4 5
# 4 5 6
some_tuple = ('A', 'B', 'C')
print(some_tuple[0], some_tuple[1], some_tuple[2])
# A B C
И с распаковкой:
some_dict = {
1: (1, 2, 3),
2: (3, 4, 5),
3: (5, 6, 7)
for value_1, value_2, value_3 in some_dict.values():
print(value_1, value_2, value_3)
# 1 2 3
# 3 4 5
# 4 5 6
some_tuple = ('A', 'B', 'C')
value_A, value_B, value_C = some_tuple
print(value_A, value_B, value_C)
# 1 2 3
Вариант с распаковкой получается читаимее, потому что переменные можно осмысленно называть.
Функция range()
В Python есть встроенная функция, которая возвращает кортеж из N чисел – range(n). Её очень удобно использовать, если ты хочешь сделать что-то определённое количество раз. Пример:
for i in range(5):
print(i)
# 0
# 1
# 2
# 3
# 4
На самом деле она возвражает не кортеж, а генератор, но это мы пока опустим.
Обрати внимание, что мы указали 5, и прошли все числа от 0 до 4, не включая саму 5.
Ещё, если ты укажешь два числа, то можно начать не с 0, а с какого тебе надо числа:
for i in range(2, 5):
print(i)
# Вывод:
# 2
# 3
# 4
Пропуск итерации и прерывание цикла
В Python (и других языках), разработчику предоставляется возможность пропустить текущую итерацию цикла или прервать выполнение всего цикла.
Пропуск итерации обычно используется вместе с условием, чтобы не выполнять какое-то действие при определённом условии. Например:
for i in range(5):
if i == 0 or i == 2 or i == 4:
continue
print(i)
# 1
# 3
Прерывание цикла обычно необходимо в том случае, если мы уже получили тот результат, который хотели, и дальнейшее выполнение цикла бесполезно. Пример:
some_list = [1, 55, 342, 774]
valueToFind = 342
hasValue = False
for value in some_list:
print('Checking value:', value)
if value == valueToFind:
hasValue = True
break
if hasValue:
print('Found value:', valueToFind)
else:
print('Value', valueToFind, 'not found')
# Checking value: 1
# Checking value: 55
# Checking value: 342
# Found value: 342
Цикл в цикле
Зачастую, при обработке каких-то данных, тебе необходимо сравнивать между собой сразу несколько вещей.
Пример – ты проектировщик домов. Заказчик сказал, что хочет прямоугольный дом с площадью 100 квадратных метров. Тебе нужно найти при каких длинах сторон дома получится такая площадь.
Вот решение такой задачи:
area = 100
for width in range(1, 101): # До 101 потому что потому что само число не включаем
for length in range(1, 101):
if width * length == area:
print(width, 'x', length)
# 1 x 100
# 2 x 50
# 4 x 25
# 5 x 20
# 10 x 10
# 20 x 5
# 25 x 4
# 50 x 2
# 100 x 1
В первом цикле я перебираю возможную ширину дома, во втором возможную длину, и проверяю какая из всех комбинаций подойдёт под указанную площадь:
- width=1,length=1 – не подходит
- width=1,length=2 – не подходит
- …
- width=1,length=100 – подходит
- range во втором цикле дошёл до конца – завершаем проход по второму циклу, и переходим к следующей итерации в первом цикле
- width=2,length=1 – не подходит
- …
- width=100,length=100 – не подходит
- Завершили весь проход по первому циклу – выходим из него
Задание на закрепление цикла for
Задание “стипендия”
- Есть словарь с данными студентов: ключ – ID студента, значение – словарь с (ФИО, оценками по всем предметам, посещаемостью)
- Необходимо составить несколько списков студентов:
- список на отчисление – средняя оценка меньше или равна 3.5 и посещаемость меньше или равна 0.7
- список на стипендию – средняя оценка выше или равна 4 и посещаемость выше или равна 0.9
- список на повышенную степендию – средняя оценка равна 5 и посещаемость 1
- список студентов, которые не попали в другие списки
- Выведи отдельно имена студентов из всех списков
- Если студент находится в одном из списков, то его не должно быть в другом списке
Вот словарь с данными: файл
####Небольшая помощь, если есть трудности с этим огромным словарем
В этой задаче тебе придется работать со вложенными словарями – это не сильно отличается от работы с обычным словарем, просто добавляется еще один цикл:
students = {
0: {'ФИО': 'Беляева Тамара Рудольфовна',
'оценки': {'Математический анализ': 5,
'Операционные системы': 5,
'Программирование на Python': 5,
'Робототехника': 5},
'посещаемость': 1},
1: {'ФИО': 'Бобылёва Келен Матвеевна',
'оценки': {'Математический анализ': 4,
'Операционные системы': 5,
'Программирование на Python': 5,
'Робототехника': 5},
'посещаемость': 0.5685316350884995},
}
# Можно обработать оценки каждого студента в отдельности
for student_id, student in students.items():
print('Name:', student['ФИО'])
for subject, mark in student['оценки'].items():
print(subject, mark)
# Можно вытащить конкретную оценку для конкретного студента
print(students[0]['оценки']['Программирование на Python'])
Цикл while
Вот пример определения цикла while:
value = 1
# Читается так: пока value < 10 делай
while value < 10:
print(value, value*value)
value += 1
# Квадрат числа 10 не выведется потому что условие "value < 10" уже не выполнится, когда value будет равен 10
# 1 1
# 2 4
# 3 9
# 4 16
# 5 25
# 6 36
# 7 49
# 8 64
# 9 81
Цикл while используется реже чем for, но он тоже необходим для решения определённых задач. Вот пример:
import random
# На входе произвольное число от 1 до 100
value = random(1, 100)
# Нужно вывести все числа, которые делятся на 5 и 9
while value > 0:
if value % 5 == 0 or value % 9 == 0:
print(value)
# Мы будем вычитать на каждом проходе цикла по единице, и так пройдём по всем оставшимся
# значениям до момента, когда value будет равен 0 - тогда мы выйдем из цикла
value -= 1
Оператор остаток от деления
Сейчас я использовал оператор, который ранее мы не встречали – это оператор “остаток от деления”. Он возвращает остаток после деления на заданное число – например:
12 % 5 # (12 / 5) - 2 целых, в остатке 2 -- оператор вернёт 2
4 % 6 # (4 / 6) - 0 целых, в остатке 4 -- оператор вернёт 4
25 % 5 # (25 / 5) - 5 целых, в остатке 0 -- оператор вернёт 0
for i in range(5):
if i % 2 == 0:
continue
# 1
# 3
Задание на закрепление
Это очень сложное задание – приступай к нему, если хочешь бросить себе вызов (и у тебя есть свободное время). Можешь спокойно его пропустить, ничего страшного не произойдёт :)
Задание “взлом шифрования”:
Теория: * Один из самых популярных алгоритмов шифрования RSA работает за счёт произведения операций с простыми числами * Простые числа – числа, которые делятся только на себя и на 1 (и не является 1). Например: число 23 – простое, а число 27 – не простое (27 делится на 3 и 9) * В алгоритме шифрования RSA берётся 2 простых числа (приватный ключ) и перемножается, например: 13 * 23 == 299 * Результат этого перемножения является публичным ключом, который используется для шифрования сообщений и является общедоступным Задача: зная публичный ключ 3403, найти из каких простых чисел он был создан (приватный ключ). Известно, что для его создания были использованы два простых числа, которые меньше 100 Практика: * С помощью while пройди по всем числам от 1 до 100 * Потом придумай как ты можешь проверить что число в текущей итерации является простым (на картинке ниже я легко подтолкнул в нужном направлении) * Составь список всех простых чисел до 100 * Затем надо по очереди перемножить все простые числа друг с другом и сравнить с известным публичным ключом 3403, чтобы найти нужную комбинацию из двух чисел
Вот пояснение, которое может тебе помочь:
В этом примере было использовано два простых числа до 100, но в реальном мире для шифрования используются два числа, которые НАМНОГО больше 100. Вот, например, публичный ключ, которым шифрует свои данные google (можете попробовать подобрать два исходных простых числа, это займёт всего пару тысячелетий): 6200775413508788707869143248474167549822107185425627296325645173604849030956 7524007579084986705000752601808399633539399372401604287204692259087208312746262
Это одно число, я его просто разбил на два, а то не влезало в экран :)
Заключение
Итого, мы изучили:
- Цикл for
- Распаковка
- Пропуск итерации и прерывание цикла
- Функция range()
- Цикл while
- Простые числа
Если что – пиши, я помогу и постараюсь объяснить лучше.
Дальше мы рассмотрим функции – с помощью них ты сможешь разбить логику программы на отдельные блоки и меньше путаться при написании сложных программ.