Модуль 03 — Условия и циклы

Программа, которая принимает решения и повторяет действия

Прогресс курса Модуль 3 из 9

Что вы освоите в этом модуле

01

Ветвление if

if выполняет блок, только если условие истинно. Блок — строки с одинаковым отступом после двоеточия. elif («иначе если») проверяется, только когда все условия выше были ложны; else — когда не подошло ничего.

# Перевод баллов (0–100) в оценку
score = int(input("Баллы: "))

if score < 0 or score > 100:
    print("Ошибка: баллы должны быть от 0 до 100")
elif score >= 85:
    print("Оценка: 5 (отлично)")
elif score >= 70:
    print("Оценка: 4 (хорошо)")
elif score >= 55:
    print("Оценка: 3 (удовлетворительно)")
else:
    print("Оценка: 2 (не зачтено)")
$ python3 grade.py
Баллы: 92
Оценка: 5 (отлично)
$ python3 grade.py
Баллы: 70
Оценка: 4 (хорошо)
$ python3 grade.py
Баллы: 54
Оценка: 2 (не зачтено)
$ python3 grade.py
Баллы: 120
Ошибка: баллы должны быть от 0 до 100

Python проверяет условия сверху вниз и выполняет первую подходящую ветку — остальные уже не смотрит. Поэтому 70 баллов дали «4»: условие score >= 85 ложно, score >= 70 истинно, до проверки на 55 дело не дошло. Отсюда правило: от узкого условия к широкому. Если порядок перепутать, программа работает, но неверно:

score = int(input("Баллы: "))
if score >= 55:
    print("Оценка: 3")
elif score >= 70:
    print("Оценка: 4")
elif score >= 85:
    print("Оценка: 5")
$ python3 grade_bad.py
Баллы: 92
Оценка: 3

92 больше 55, первая же ветка сработала. Проверку некорректных данных (баллы вне 0–100) ставят первой — чтобы дальше работать только с правильными значениями.

02

Логические операции

and — истинно, если истинны обе части; or — если истинна хотя бы одна; not — отрицание. Сравнения можно сцеплять: 14 <= age < 18 означает «age от 14 включительно до 18».

>>> age = 17
>>> age >= 18
False
>>> 14 <= age < 18
True
>>> age >= 18 or age == 17
True
>>> not age >= 18
True
>>> x = 0
>>> x != 0 and 10 / x > 1
False
>>> 10 / x > 1 and x != 0
Traceback (most recent call last):
  File "<python-input-7>", line 1, in <module>
    10 / x > 1 and x != 0
    ~~~^~~
ZeroDivisionError: division by zero
>>> name = ""
>>> name or "гость"
'гость'

Обе логические операции ленивые: and не вычисляет правую часть, если левая ложна, or — если левая истинна. Поэтому x != 0 and 10 / x > 1 безопасно, а то же самое в обратном порядке падает с делением на ноль. Проверка, которая защищает от ошибки, ставится слева.

or возвращает не True, а первое истинное значение — отсюда приём «значение по умолчанию»: name or "гость".

Приоритет: not выше and, and выше or. Правило високосного года — «делится на 4 и не делится на 100, или делится на 400» — записывается дословно:

year = int(input("Год: "))
if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
    print(year, "— високосный, в феврале 29 дней")
else:
    print(year, "— обычный, в феврале 28 дней")
$ python3 leap.py
Год: 2028
2028 — високосный, в феврале 29 дней
$ python3 leap.py
Год: 1900
1900 — обычный, в феврале 28 дней
$ python3 leap.py
Год: 2000
2000 — високосный, в феврале 29 дней

Для читателя лучше поставить скобки, даже если они не нужны интерпретатору: (year % 4 == 0 and year % 100 != 0) or year % 400 == 0.

03

Цикл for и range

for перебирает элементы последовательности: на каждом шаге переменная цикла получает следующий элемент и выполняется тело. Самая частая последовательность — range:

ЗаписьЧисла
range(5)0, 1, 2, 3, 4 — от нуля, не включая 5
range(1, 11)1 … 10
range(1, 11, 3)1, 4, 7, 10 — с шагом 3
range(10, 0, -3)10, 7, 4, 1 — обратный отсчёт
>>> list(range(5))
[0, 1, 2, 3, 4]
>>> list(range(1, 11, 3))
[1, 4, 7, 10]
>>> list(range(10, 0, -3))
[10, 7, 4, 1]
>>> for i in range(3):
...     print("Повтор", i)
... 
Повтор 0
Повтор 1
Повтор 2

list(range(...)) здесь только для того, чтобы увидеть числа: сам range их не хранит, а выдаёт по одному. Поэтому range(1_000_000_000) не занимает память. Правая граница не включается — это сделано, чтобы range(n) давал ровно n шагов.

for перебирает не только числа: по строке — символы, по списку — элементы (модули 04 и 05).

04

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

Цикл внутри цикла: на каждый шаг внешнего внутренний проходит целиком. Таблица умножения — внешний цикл по строкам, внутренний по столбцам:

# Таблица умножения 1..5
for row in range(1, 6):
    for col in range(1, 6):
        print(f"{row * col:4}", end="")
    print()
$ python3 table.py
   1   2   3   4   5
   2   4   6   8  10
   3   6   9  12  15
   4   8  12  16  20
   5  10  15  20  25

end="" оставляет числа строки рядом, print() после внутреннего цикла переводит строку. Формат {…:4} — ширина 4, числа выравниваются по правому краю. Внутреннее тело выполнилось 5 × 5 = 25 раз: вложенные циклы быстро умножают работу, и на больших данных это заметно.

05

Цикл while

while повторяет тело, пока условие истинно. Его выбирают, когда заранее неизвестно число шагов: пока в числе есть цифры, пока пользователь не угадал, пока файл не кончился.

# Сумма цифр числа
n = int(input("Число: "))
total = 0
while n > 0:
    total += n % 10
    n //= 10
print("Сумма цифр:", total)
$ python3 digits.py
Число: 2026
Сумма цифр: 10
$ python3 digits.py
Число: 987654321
Сумма цифр: 45

n % 10 — последняя цифра, n //= 10 — отбросить её. Когда цифр не осталось, n становится 0 и цикл заканчивается.

Если тело while не меняет то, от чего зависит условие, цикл не закончится никогда. Остановить такую программу — Ctrl+C. Перед запуском спросите себя: что в теле приближает выход?
count = 10
while count > 0:
    print(count)
    count + 1        # ошибка: значение вычислено и выброшено, count не меняется

Нужно count -= 1. Ошибка двойная: и операция не та, и результат не присвоен.

06

break и continue

break немедленно завершает цикл. continue завершает текущий шаг и переходит к следующему. Типичный приём — while True с выходом через break: условие окончания проверяется в середине тела, когда нужные данные уже получены.

import random

secret = random.randint(1, 100)
tries = 0
print("Я загадал число от 1 до 100.")

while True:
    guess = int(input("Ваш вариант: "))
    tries += 1
    if guess < secret:
        print("Больше")
    elif guess > secret:
        print("Меньше")
    else:
        print(f"Угадали за {tries} попыток")
        break
$ python3 guess.py
Я загадал число от 1 до 100.
Ваш вариант: 50
Меньше
Ваш вариант: 25
Меньше
Ваш вариант: 12
Больше
Ваш вариант: 18
Меньше
Ваш вариант: 15
Больше
Ваш вариант: 16
Угадали за 6 попыток

Модуль random подключается строкой import random (подробно — в модуле 06); random.randint(1, 100) — случайное целое от 1 до 100 включительно. Деление отрезка пополам гарантирует, что любое число от 1 до 100 угадывается не больше чем за 7 попыток.

continue удобен, чтобы отсеять неподходящее, не заворачивая всё остальное тело в if:

# Среднее положительных чисел; пустая строка — конец ввода
total = 0
count = 0
while True:
    line = input("Число (Enter — закончить): ")
    if line == "":
        break
    number = float(line)
    if number <= 0:
        print("  пропускаю: нужно положительное")
        continue
    total += number
    count += 1

if count > 0:
    print(f"Чисел: {count}, среднее: {total / count:.2f}")
else:
    print("Чисел не было")
$ python3 average.py
Число (Enter — закончить): 4
Число (Enter — закончить): -2
  пропускаю: нужно положительное
Число (Enter — закончить): 5.5
Число (Enter — закончить): 0
  пропускаю: нужно положительное
Число (Enter — закончить): 10
Число (Enter — закончить): 
Чисел: 3, среднее: 6.50
$ python3 average.py
Число (Enter — закончить): 
Чисел не было

Проверка count > 0 после цикла — не формальность: без неё пустой ввод закончился бы делением на ноль.

07

else у цикла

У for и while может быть ветка else: она выполняется, если цикл завершился сам, без break. Это естественная запись поиска: «перебрал всё и не нашёл».

n = int(input("Число: "))
for d in range(2, n):
    if n % d == 0:
        print(n, "— составное, делится на", d)
        break
else:
    print(n, "— простое")
$ python3 prime.py
Число: 91
91 — составное, делится на 7
$ python3 prime.py
Число: 97
97 — простое

Без else понадобилась бы переменная-флаг found = False, которую цикл переключает. Конструкция редкая и путает тех, кто её не знает, — используйте, но не злоупотребляйте.

08

match: выбор по значению

Когда одно значение сравнивается со многими вариантами, вместо длинной цепочки elif можно написать match (Python 3.10 и новее). | — «или», _ — всё остальное.

>>> command = "stop"
>>> match command:
...     case "start" | "run":
...         print("Запускаю")
...     case "stop":
...         print("Останавливаю")
...     case _:
...         print("Неизвестная команда")
... 
Останавливаю

Это та же цепочка if command == "start" or command == "run": … elif …, записанная короче. У match есть и более мощные возможности — разбор структуры данных по образцу, — но для начала достаточно выбора по значению.

Ключевые выводы модуля

Запомните главное

  • if / elif / else проверяются сверху вниз до первой подходящей ветки: от узкого условия к широкому
  • and и or ленивые: защитную проверку ставьте слева
  • range(a, b) не включает b; range(n) — ровно n шагов от нуля
  • for — когда известно, что перебирать; while — когда известно условие остановки
  • В теле while должно меняться то, от чего зависит условие
  • break — выйти из цикла, continue — к следующему шагу, else у цикла — «прошёл без break»
Практические задания — на учебном портале. Задания, критерии оценивания, сдача работ и оценки преподавателя — в курсе на portal.nevabit.ru. Учётную запись выдаёт преподаватель.
Модуль 02: Типы и выражения Все модули Модуль 04: Строки