Урок 4. Умови та цикли: як програма приймає рішення¶
Досі програма виконувала рядки один за одним, зверху вниз, і завжди робила те саме. Справжні програми поводяться інакше: вони отримують дані, перевіряють умови, обирають дію і повторюють її, поки це потрібно. Саме цього навчає цей урок.
Що потрібно з уроку 3: змінні, типи int, float, str, перетворення int() і str(), різниця між 42 і "42".
Після уроку ти зможеш:
- записувати умови через порівняння
==,!=,<,<=,>,>=і поєднувати їх черезand,or,not; - обирати одну дію з кількох через
if/elif/elseі пояснювати, чому порядок перевірок важливий; - відрізняти «значення немає» (
None) від нуля та порожнього рядка; - отримувати дані від користувача через
input()і готувати їх до перевірки; - повторювати дії циклом
while, змінювати стан і вчасно зупиняти цикл черезbreakабоcontinue.
Задача розділу. Кафе приймає замовлення. Офіціант вводить позиції з меню, програма рахує суму, а наприкінці вирішує, чи можлива доставка і скільки вона коштує. Ми зберемо цю програму крок за кроком, а повний код розберемо в розділі «Практика».
Ноутбук заняття: Відкрити вправи в Colab Переглянути розв’язки
Пригадай¶
Дай відповідь подумки або на папері, нічого не запускаючи:
- Який тип поверне
type(3.14)? - Скільки буде
int("7") + int("3")? str(5) == "5"— цеTrueчиFalse?
Відповіді
<class 'float'>— у числа є дробова частина.10. Обидва рядки спершу перетворено на числа, тому+додає, а не склеює (безint()вийшло б"73").True.str(5)дає рядок"5", і порівнюються два однакові рядки.
True, False і порівняння¶
Кожне рішення програми починається з питання, на яке є лише дві відповіді: так або ні. Для цього в Python є тип bool з двома значеннями: True (істина) і False (хиба). Пишуться вони з великої літери.
Такі значення найчастіше отримують порівнянням:
| Оператор | Значення | Приклад → результат |
|---|---|---|
== |
дорівнює | 500 == 500 → True |
!= |
не дорівнює | "кава" != "чай" → True |
< |
менше | 150 < 200 → True |
<= |
менше або дорівнює | 200 <= 200 → True |
> |
більше | 150 > 200 → False |
>= |
більше або дорівнює | 500 >= 500 → True |
Результат порівняння — звичайне значення типу bool. Його можна надрукувати, записати в змінну і, головне, використати в умові.
= і == — різні речі¶
Один знак = присвоює: зв'язує ім'я зі значенням. Два знаки == порівнюють і повертають True або False. Якщо переплутати їх в умові, Python не запустить програму:
Порівняння рядків враховує регістр: "Кава" == "кава" дає False. Це знадобиться, коли будемо обробляти введення користувача.
Ланцюжок порівнянь¶
Щоб перевірити, чи потрапляє значення в діапазон, у Python можна записати два порівняння одним ланцюжком:
Запис 18 <= age < 65 означає те саме, що 18 <= age and age < 65: Python порівнює кожну сусідню пару.
Передбач результат, перш ніж відкрити відповідь:
Відповідь
Ланцюжок читається як 1 == 2 and 2 < 3. Перша частина 1 == 2 хибна, тому весь вираз — False. Це не (1 == 2) < 3.
Ще одна пара, яку варто передбачити самому:
Відповідь
Без дужок це ланцюжок: 1 < 3 and 3 > 2 — обидві частини істинні. З дужками спершу обчислюється (1 < 3), тобто True, а далі True > 2. True поводиться як 1 (див. примітку нижче), і 1 > 2 хибне. Дужки перетворили ланцюжок на два окремі порівняння.
Два уточнення
bool— підтипint:Trueповодиться як1, аFalse— як0. ТомуTrue + Trueдає2, а0 == FalseдаєTrue. У звичайному коді на цьому не будують логіку, але це пояснює деякі несподівані результати.- Не складай ланцюжки з
!=. Вираз"cat" != "dog" != "cat"даєTrue, хоча перше і третє значення однакові: перевіряються лише сусідні пари.
Парне чи непарне: остача від ділення¶
Оператор % з уроку 3 дає остачу від ділення. Число парне, коли остача від ділення на 2 дорівнює нулю:
Цикл for лише перебирає три приклади (урок 6). Для -7 Python дає -7 % 2 == 1: остача має знак дільника, тому для від'ємних непарних чисел вона теж 1. Надійна перевірка непарності — number % 2 != 0, вона працює для будь-яких цілих.
Умови: if, elif, else¶
Одна умова¶
Повернімося до кафе. Доставка можлива лише для замовлень від 200 грн:
order_total = 220
if order_total >= 200:
print("Доставку можна оформити")
print("Перевірку завершено")
Як це виконується:
- Python обчислює умову
order_total >= 200і отримуєTrue. - Оскільки умова істинна, виконується блок — рядки з відступом під
if. - Рядок
print("Перевірку завершено")не має відступу, тому він не належить доifі виконується завжди.
Якби order_total дорівнював 150, програма надрукувала б лише «Перевірку завершено».
Відступ — це не оформлення, а частина мови: саме він показує, які рядки належать до блоку. Стандарт — 4 пробіли. Після двокрапки блок обов'язковий:
Кілька варіантів: elif і else¶
Тепер правило доставки складніше:
- від 500 грн — безкоштовно;
- від 200 грн — 60 грн;
- менше — доставка недоступна.
order_total = 220
if order_total >= 500:
print("Доставка: безкоштовно")
elif order_total >= 200:
print("Доставка: 60 грн")
else:
print("Мінімальне замовлення для доставки — 200 грн")
elif означає «інакше, якщо», else — «в усіх інших випадках». Python перевіряє умови зверху вниз і виконує першу гілку, умова якої істинна. Після цього решта гілок пропускається, їхні умови навіть не обчислюються. Якщо жодна умова не спрацювала, виконується else.
flowchart TD
classDef step fill:#eceff1,stroke:#546e7a,stroke-width:1px,color:#1a1a1a;
classDef decision fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#1a1a1a;
classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1a1a1a;
classDef error fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#1a1a1a;
classDef warning fill:#fff8e1,stroke:#e65100,stroke-width:2px,color:#1a1a1a;
S["order_total"] --> C1{"order_total >= 500?"}
C1 -- так --> A1["Доставка: безкоштовно"]
C1 -- ні --> C2{"order_total >= 200?"}
C2 -- так --> A2["Доставка: 60 грн"]
C2 -- ні --> A3["else: мінімальне<br>замовлення 200 грн"]
A1 --> E["далі програма"]
A2 --> E
A3 --> E
class S,E step
class C1,C2 decision
class A1,A2 success
class A3 warning
Схема показує, що з трьох гілок завжди виконується рівно одна: друга умова перевіряється лише тоді, коли перша хибна.
Ось як проходить той самий код для різних сум:
order_total |
Перевірки | Що виведе програма |
|---|---|---|
150 |
>= 500 — ні, >= 200 — ні |
Мінімальне замовлення для доставки — 200 грн |
200 |
>= 500 — ні, >= 200 — так |
Доставка: 60 грн |
499 |
>= 500 — ні, >= 200 — так |
Доставка: 60 грн |
500 |
>= 500 — так (далі не перевіряється) |
Доставка: безкоштовно |
800 |
>= 500 — так (далі не перевіряється) |
Доставка: безкоштовно |
Зверни увагу на межі 200 і 500: оператор >= включає саму межу, тому замовлення рівно на 500 грн отримує безкоштовну доставку.
Передбач результат: порядок перевірок¶
Хтось поміняв умови місцями:
order_total = 800
if order_total >= 200:
print("Доставка: 60 грн")
elif order_total >= 500:
print("Доставка: безкоштовно")
else:
print("Мінімальне замовлення для доставки — 200 грн")
Що виведе програма для 800 грн?
Пройди умови зверху вниз і зупинись на першій істинній.
Відповідь і пояснення
800 >= 200 істинна вже на першій перевірці, тому виконується перша гілка, а elif order_total >= 500 не перевіряється зовсім. Гілка безкоштовної доставки стала недосяжною: будь-яке число, більше за 500, більше й за 200.
Правило: коли умови перевіряють межі одного значення, починай із найвужчої (найбільшої межі) — або записуй кожен діапазон повністю: 200 <= order_total < 500.
Окремі if замість elif¶
Якщо написати кожну перевірку окремим if, Python перевірить усі:
order_total = 600
if order_total >= 500:
print("Безкоштовна доставка")
if order_total >= 200:
print("Доставка 60 грн")
Клієнт отримав два суперечливі повідомлення. Окремі if доречні, коли перевірки незалежні (наприклад, «чи є коментар» і «чи потрібні прибори»). Якщо з кількох варіантів має спрацювати лише один — потрібен ланцюжок if / elif / else.
Вкладені умови¶
Усередині блоку може бути ще одна умова. Тоді її блок має подвійний відступ:
order_total = 150
is_delivery = True
if is_delivery:
if order_total >= 200:
print("Оформлюємо доставку")
else:
print("Для доставки не вистачає", 200 - order_total, "грн")
else:
print("Замовлення в залі")
Внутрішня умова перевіряється лише тоді, коли зовнішня (is_delivery) істинна. Кожен else належить тому if, під яким він стоїть з тим самим відступом.
Шлях виконання для order_total = 150, is_delivery = True. Помаранчеві вузли — перевірки, які справді виконались, зелений — гілка, що спрацювала, червоні — гілки, до яких виконання не дійшло:
flowchart TD
classDef step fill:#eceff1,stroke:#546e7a,stroke-width:1px,color:#1a1a1a;
classDef decision fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#1a1a1a;
classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1a1a1a;
classDef error fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#1a1a1a;
classDef warning fill:#fff8e1,stroke:#e65100,stroke-width:2px,color:#1a1a1a;
S["order_total = 150<br>is_delivery = True"] --> O{"is_delivery?<br>True"}
O -- так --> I{"order_total >= 200?<br>150 >= 200 → False"}
O -. ні .-> H["Замовлення в залі"]
I -. так .-> D["Оформлюємо доставку"]
I -- ні --> M["Для доставки не вистачає<br>50 грн"]
class S step
class O,I warning
class M success
class H,D error
Зовнішня перевірка відкриває або закриває вхід до внутрішньої: при is_delivery = False умова order_total >= 200 навіть не обчислювалась би.
match / case: вибір за значенням¶
Коли одна змінна порівнюється з кількома конкретними значеннями, довгий ланцюжок elif command == ... можна записати коротше. З Python 3.10 для цього є match:
for mode in ["доставка", "в залі", "таксі"]:
match mode:
case "зал" | "в залі":
print("Замовлення в залі")
case "доставка":
print("Оформлюємо доставку")
case "самовивіз":
print("Заберіть на касі")
case _:
print("Невідомий спосіб:", mode)
Цикл for тут лише перебирає три приклади (його розберемо в уроці 6). Головне — блок match:
match mode:— яке значення перевіряємо;case "доставка":— гілка спрацює, якщоmode == "доставка";|— «або»:case "зал" | "в залі":спрацює на будь-якому з двох рядків;case _:— «усе інше», якelse;- виконується перша гілка, що підійшла, решта пропускається — як у ланцюжку
elif.
flowchart LR
classDef step fill:#eceff1,stroke:#546e7a,stroke-width:1px,color:#1a1a1a;
classDef decision fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#1a1a1a;
classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1a1a1a;
classDef error fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#1a1a1a;
classDef warning fill:#fff8e1,stroke:#e65100,stroke-width:2px,color:#1a1a1a;
V["mode = #quot;таксі#quot;"] --> C1{"#quot;зал#quot; | #quot;в залі#quot;?"}
C1 -- ні --> C2{"#quot;доставка#quot;?"}
C2 -- ні --> C3{"#quot;самовивіз#quot;?"}
C3 -- ні --> C4["case _<br>Невідомий спосіб: таксі"]
class V step
class C1,C2,C3 decision
class C4 success
case _, а не case other
Голе ім'я в case не порівнюється, а захоплює будь-яке значення: case other: спрацює завжди й запише значення у змінну other. Якщо після нього є ще гілки, Python не запустить програму: SyntaxError: name capture 'other' makes remaining patterns unreachable. Для «усього іншого» пиши case _:.
match уміє більше: розбирати списки й словники за формою, додавати умову if до гілки. Усе це — в окремому ноутбуці Відкрити вправи в Colab Переглянути розв’язки. Для порівнянь на кшталт order_total >= 500 залишайся з if / elif: match — про збіг із конкретними значеннями та формою даних.
Truthy, falsy і None¶
Значення як умова¶
В if можна поставити не лише порівняння, а будь-яке значення. Тоді Python сам вирішує, вважати його істинним чи хибним. Хибними (falsy) вважаються:
FalseіNone;- нуль будь-якого числового типу:
0,0.0; - порожні рядок і колекції:
"",[](списки — тема уроку 5).
Усе інше — truthy, тобто вважається істинним. Перевірити можна функцією bool():
print(bool(0), bool(0.0), bool(""), bool(None), bool([]))
print(bool("False"), bool("0"), bool(" "))
Другий рядок часто дивує: "False", "0" і навіть рядок з одного пробілу — непорожні рядки, тому вони істинні. Python не читає зміст рядка.
Як Python вирішує, чи виконати блок if value::
flowchart TD
classDef step fill:#eceff1,stroke:#546e7a,stroke-width:1px,color:#1a1a1a;
classDef decision fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#1a1a1a;
classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1a1a1a;
classDef error fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#1a1a1a;
classDef warning fill:#fff8e1,stroke:#e65100,stroke-width:2px,color:#1a1a1a;
V["if value:"] --> N{"False або None?"}
N -- так --> F["falsy<br>блок пропускається"]
N -- ні --> Z{"нуль?<br>0, 0.0"}
Z -- так --> F
Z -- ні --> E{"порожнє?<br>#quot;#quot;, []"}
E -- так --> F
E -- ні --> T["truthy<br>блок виконується"]
class V step
class N,Z,E decision
class F error
class T success
Рядок "0" проходить усі три перевірки з відповіддю «ні»: це не None, не число і не порожній рядок.
Так зручно перевіряти, чи ввів користувач хоч щось:
bool() не розуміє відповідь «так/ні»
Якщо користувач ввів "ні", то bool("ні") дає True, бо рядок непорожній. Щоб перевірити відповідь, порівнюй текст: answer == "так".
None — «значення немає»¶
None — окреме значення, яке означає «значення відсутнє»: ще не відомо, не вказано. Це не нуль і не порожній рядок:
Перевіряють None через is None або is not None.
None має власний тип — NoneType, і це єдине значення цього типу:
None — falsy, як 0 і "", але означає інше: не «нуль», а «нічого не вказано».
У кафе це важливо для чайових: 0 означає «гість свідомо відмовився», а None — «гість ще нічого не вказав». Програма має відрізняти ці випадки:
tip_percent = None
if tip_percent is None:
print("Чайові не вказано")
elif tip_percent == 0:
print("Гість відмовився від чайових")
else:
print("Чайові:", tip_percent, "%")
tip_percent |
Результат |
|---|---|
None |
Чайові не вказано |
0 |
Гість відмовився від чайових |
10 |
Чайові: 10 % |
Типова помилка
Умова if not tip_percent: спрацює і для None, і для 0, бо обидва значення falsy. Коли нуль — допустиме значення, перевіряй саме відсутність: if tip_percent is None:.
and, or, not¶
Поєднання умов¶
a and b— істинно, коли істинні обидві умови;a or b— істинно, коли істинна хоча б одна;not a— заперечення:TrueстаєFalseі навпаки.
Студентська знижка діє, лише коли гість — студент і сума від 300 грн:
Порядок обчислення¶
Спершу обчислюються порівняння, потім not, потім and, і лише потім or. Тому вираз
Python читає як (not is_student) or ((order_total > 1000) and (guests > 4)). Щоб читачеві не доводилось згадувати цей порядок, став дужки явно — результат не зміниться, а намір стане очевидним.
Найпідступніший випадок — not поруч із порівнянням. Кафе хоче показати банер «сьогодні тихо», коли не правда, що «гостей багато і сьогодні вихідний». Колега пише:
guests = 0
is_weekend = False
print(not guests > 10 and is_weekend)
print(not (guests > 10 and is_weekend))
Передбач обидва рядки
Де Python ставить дужки в першому виразі?
Відповідь
Перший рядок Python читає як (not (guests > 10)) and is_weekend: not чіпляється лише до порівняння, а and — до всього, що праворуч. True and False дає False, хоча гостей нуль і банер мав би з'явитися. Другий рядок записує задум явно: заперечуємо всю умову в дужках. Якщо not має стосуватися кількох умов — дужки обов'язкові.
Коротке обчислення¶
Python зупиняє обчислення, щойно результат відомий. Для and: якщо ліва частина хибна, права не обчислюється. Для or: якщо ліва частина істинна, права не обчислюється.
Це дозволяє робити захисну перевірку. Порахуймо середній чек на гостя, коли гостей може бути нуль:
guests = 0
order_total = 300
if guests > 0 and order_total / guests > 200:
print("Великий чек на гостя")
else:
print("Середній чек не рахуємо")
guests > 0 хибна, тому ділення order_total / guests не виконується взагалі. Якщо поміняти частини місцями (order_total / guests > 200 and guests > 0), ділення відбудеться першим і програма впаде: ZeroDivisionError: division by zero.
Обидва порядки поруч, при guests = 0:
flowchart TD
classDef step fill:#eceff1,stroke:#546e7a,stroke-width:1px,color:#1a1a1a;
classDef decision fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#1a1a1a;
classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1a1a1a;
classDef error fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#1a1a1a;
classDef warning fill:#fff8e1,stroke:#e65100,stroke-width:2px,color:#1a1a1a;
subgraph OK["guests > 0 and order_total / guests > 200"]
direction LR
A1{"guests > 0<br>0 > 0 → False"} --> A2["and уже знає відповідь<br>False"]
A2 --> A3["ділення не виконується<br>гілка else"]
end
subgraph BAD["order_total / guests > 200 and guests > 0"]
direction LR
B1["order_total / guests<br>300 / 0"] --> B2["ZeroDivisionError<br>програма падає"]
end
OK ~~~ BAD
class A1 warning
class A2 step
class A3 success
class B1 warning
class B2 error
Ліва частина and працює як охоронець: права частина обчислюється, лише коли ліва істинна.
and і or повертають операнд¶
not завжди повертає True або False. А and і or повертають один зі своїх операндів, не обов'язково bool:
x or yповертаєx, якщоxtruthy, інакшеy;x and yповертаєx, якщоxfalsy, інакшеy.
В умові if це непомітно: Python однаково перевіряє, truthy чи falsy результат. Але якщо записати результат у змінну, отримаєш саме операнд.
Дві поширені пастки
1. or з голим рядком. Це виглядає як «відповідь так або т», але працює інакше:
Програма надрукує «Підтверджено». Вираз читається як (answer == "так") or "т", а непорожній рядок "т" завжди truthy. Правильно — порівнювати обидва рази: answer == "так" or answer == "т".
2. or як «значення за замовчуванням», коли 0 допустимий.
Виведе 10, хоча гість явно вказав 0: or не відрізняє нуль від відсутнього значення. Якщо «немає» позначено None, перевіряй саме його:
Дані від користувача: input()¶
input() показує підказку, чекає, поки користувач введе текст і натисне Enter, і повертає введене. Результат завжди рядок, навіть якщо ввели цифри:
Щоб рахувати, рядок треба явно перетворити через int() або float(). Інакше + склеїть текст: "3" + "2" дає "32".
А спроба додати до введеного число зупинить програму:
TypeError каже: операція не підходить для цих типів. Рядок і число не додаються — спершу int(guests_text).
Очищення тексту¶
Люди вводять з пробілами й різним регістром: " Кава", "КАВА ". Перед порівнянням текст приводять до одного вигляду:
.strip()прибирає пробіли на початку й у кінці;.lower()переводить у нижній регістр.
Методи виконуються зліва направо: спершу input() повертає " КАВА ", потім .strip() дає "КАВА", а .lower() — "кава".
Коли число не число¶
int() приймає лише рядок, схожий на ціле число (пробіли по краях він ігнорує: int(" 3 ") дає 3). На іншому тексті програма зупиняється:
int("два") # ValueError: invalid literal for int() with base 10: 'два'
int("12.5") # ValueError: invalid literal for int() with base 10: '12.5'
Кожен із цих рядків окремо зупиняє програму з ValueError: після першої помилки наступні рядки вже не виконуються.
Для "12.5" потрібен float("12.5"). Як перехоплювати такі помилки, розберемо в уроці 14. Поки що можна перевірити текст перед перетворенням:
guests_text = input("Скільки гостей? ").strip()
if guests_text.isdigit():
guests = int(guests_text)
print("Гостей:", guests)
else:
print("Введи кількість цифрами, наприклад 3")
Межі цієї перевірки
.isdigit() повертає True, лише якщо рядок непорожній і кожен символ — цифра. Тому вона відкидає "-3", "12.5" і " 3" (пробіл — не цифра; тому спершу .strip()). Для кількості гостей це підходить. Від'ємні й дробові числа краще перевіряти через винятки — урок 14.
while і стан програми¶
Цикл із лічильником¶
Кухня готує три порції. Програма має повторити дію «приготувати порцію» потрібну кількість разів:
portions = 3
cooked = 0
while cooked < portions:
cooked += 1
print("Готова порція", cooked)
print("Усі порції готові")
cooked += 1 — коротший запис cooked = cooked + 1: збільшити значення на 1.
while перевіряє умову перед кожним повтором (ітерацією). Поки умова істинна, виконується блок; щойно вона стає хибною, програма переходить до першого рядка після циклу. Таблиця показує, як змінюється стан — значення змінних:
cooked |
Умова cooked < portions |
Що відбувається |
|---|---|---|
0 |
0 < 3 → True |
cooked стає 1, друк «Готова порція 1» |
1 |
1 < 3 → True |
cooked стає 2, друк «Готова порція 2» |
2 |
2 < 3 → True |
cooked стає 3, друк «Готова порція 3» |
3 |
3 < 3 → False |
вихід із циклу, друк «Усі порції готові» |
Кожен цикл while складається з чотирьох частин: початковий стан → перевірка → дія → зміна стану.
flowchart TD
classDef step fill:#eceff1,stroke:#546e7a,stroke-width:1px,color:#1a1a1a;
classDef decision fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#1a1a1a;
classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1a1a1a;
classDef error fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#1a1a1a;
classDef warning fill:#fff8e1,stroke:#e65100,stroke-width:2px,color:#1a1a1a;
I["початковий стан<br>portions = 3, cooked = 0"] --> C{"cooked < portions?"}
C -- так --> U["зміна стану<br>cooked += 1"]
U --> P["дія<br>print('Готова порція', cooked)"]
P --> C
C -- ні --> D["Усі порції готові"]
class I,P step
class C decision
class U warning
class D success
Стрілка від дії повертається до перевірки: після кожного повтору Python знову обчислює cooked < portions. Вийти з циклу можна лише гілкою «ні», тому зміна стану (cooked += 1) — обов'язкова частина.
Та сама програма покроково: кожен блок — один прохід, у вузлах — стан змінних:
flowchart TD
classDef step fill:#eceff1,stroke:#546e7a,stroke-width:1px,color:#1a1a1a;
classDef decision fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#1a1a1a;
classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1a1a1a;
classDef error fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#1a1a1a;
classDef warning fill:#fff8e1,stroke:#e65100,stroke-width:2px,color:#1a1a1a;
subgraph P1["прохід 1"]
direction LR
C1{"0 < 3 → True"} --> U1["cooked = 1<br>Готова порція 1"]
end
subgraph P2["прохід 2"]
direction LR
C2{"1 < 3 → True"} --> U2["cooked = 2<br>Готова порція 2"]
end
subgraph P3["прохід 3"]
direction LR
C3{"2 < 3 → True"} --> U3["cooked = 3<br>Готова порція 3"]
end
subgraph P4["перевірка 4"]
direction LR
C4{"3 < 3 → False"} --> E["вихід з циклу<br>Усі порції готові"]
end
P1 --> P2 --> P3 --> P4
class C1,C2,C3 decision
class U1,U2,U3 step
class C4 warning
class E success
Умова перевіряється чотири рази, а блок виконується три: остання перевірка лише вирішує, що час виходити.
Нуль повторів¶
Якщо умова хибна з самого початку, блок не виконується жодного разу:
portions = 0
cooked = 0
while cooked < portions:
cooked += 1
print("Готова порція", cooked)
print("Усі порції готові")
0 < 0 хибна вже на першій перевірці. На відміну від деяких інших мов, у Python немає циклу, який «завжди виконується хоча б раз».
Помилка на одиницю¶
Один символ в умові змінює кількість повторів:
Скільки порцій приготує кухня?
Склади таблицю, як вище, і дійди до перевірки, яка поверне False.
Відповідь
При cooked = 3 умова 3 <= 3 ще істинна, тому виконується четвертий повтор. Така помилка на одиницю (off-by-one) — одна з найчастіших у циклах. Перевіряй межі: що станеться на останньому допустимому значенні і на першому недопустимому.
Нескінченний цикл¶
Не запускай цей код без потреби
cooked ніколи не змінюється, тому 0 < 3 залишається істинною назавжди, і програма друкує той самий рядок без кінця. У терміналі зупини її клавішами Ctrl+C, у Jupyter чи Colab — кнопкою зупинки клітинки. Виправлення — повернути зміну стану cooked += 1.
Якщо цикл поводиться дивно, додай у блок тимчасовий print() зі значеннями змінних та умови. Так видно стан на кожному кроці.
break і continue¶
break — вийти з циклу¶
break негайно завершує найближчий цикл, у якому він стоїть. Програма продовжується з першого рядка після циклу.
Це зручно, коли кількість повторів заздалегідь невідома. Офіціант вводить позиції, доки не напише готово:
while True:
command = input("Позиція: ").strip().lower()
if command == "готово":
break
print("Додано:", command)
print("Замовлення прийнято")
while True — умова, яка завжди істинна. Тому вийти з такого циклу можна лише через break. Без нього цикл був би нескінченним.
Нехай офіціант вводить кава, чай, готово. Програма виведе:
Покроково:
flowchart TD
classDef step fill:#eceff1,stroke:#546e7a,stroke-width:1px,color:#1a1a1a;
classDef decision fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#1a1a1a;
classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1a1a1a;
classDef error fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#1a1a1a;
classDef warning fill:#fff8e1,stroke:#e65100,stroke-width:2px,color:#1a1a1a;
subgraph R1["повтор 1"]
direction LR
I1["command = #quot;кава#quot;"] --> Q1{"== #quot;готово#quot;?<br>ні"} --> A1["Додано: кава"]
end
subgraph R2["повтор 2"]
direction LR
I2["command = #quot;чай#quot;"] --> Q2{"== #quot;готово#quot;?<br>ні"} --> A2["Додано: чай"]
end
subgraph R3["повтор 3"]
direction LR
I3["command = #quot;готово#quot;"] --> Q3{"== #quot;готово#quot;?<br>так"} --> B["break"]
end
R1 --> R2 --> R3 --> END["Замовлення прийнято"]
class I1,I2,I3,A1,A2 step
class Q1,Q2 decision
class Q3,B warning
class END success
Рядок print("Додано:", command) у третьому повторі вже не виконується: break виходить з циклу одразу.
while … else — варто знати, що таке буває¶
У циклу while може бути гілка else. Вона виконується, коли цикл завершився сам — умова стала хибною, — і не виконується, якщо вийшли через break:
attempts = 3
while attempts > 0:
print("Спроба", attempts)
attempts -= 1
else:
print("Спроби закінчились")
Це зручно в пошуку: «знайшли — break, не знайшли за всі спроби — else». Трапляється while … else рідко, тому досить упізнавати його в чужому коді. Повний приклад — у задачі «Вгадай число» в розділі «Практика».
continue — до наступної перевірки¶
continue завершує поточний повтор: решта блоку пропускається, і while знову перевіряє умову. Коли офіціант вводить позицію, якої немає в меню, її не треба додавати до суми — лише попередити:
flowchart TD
classDef step fill:#eceff1,stroke:#546e7a,stroke-width:1px,color:#1a1a1a;
classDef decision fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#1a1a1a;
classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1a1a1a;
classDef error fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#1a1a1a;
classDef warning fill:#fff8e1,stroke:#e65100,stroke-width:2px,color:#1a1a1a;
W{"while True"} --> IN["command = input(...)<br>.strip().lower()"]
IN --> Q1{"command == 'готово'?"}
Q1 -- так --> BR["break"]
Q1 -- ні --> Q2{"позиція є в меню?"}
Q2 -- ні --> CO["повідомлення<br>continue"]
CO --> W
Q2 -- так --> ADD["order_total += price<br>items += 1"]
ADD --> W
BR --> END["підсумок і доставка"]
class IN,ADD step
class W,Q1,Q2 decision
class CO warning
class BR,END success
continue повертає виконання до перевірки циклу, минаючи додавання ціни. break виводить за межі циклу до підсумку. Повний код цієї програми — у розділі «Практика».
Пастка: continue пропускає зміну стану¶
Кафе виводить вільні столики з 1 по 6. Столик 4 зарезервовано, його треба пропустити:
Тепер той самий задум, але лічильник збільшується наприкінці блоку:
Що станеться, коли table дорівнюватиме 4?
Простеж, чи дійде виконання до рядка table += 1.
Відповідь
Програма надрукує столики 1, 2, 3 і зависне. Коли table == 4, continue одразу повертає до перевірки 4 <= 6, а рядок table += 1 не виконується. Стан більше не змінюється, і цикл стає нескінченним.
Виправлення: змінюй стан до continue, як у першій версії (table += 1 на початку блоку). Загальне правило: перед кожним continue перевір, чи оновилися змінні, від яких залежить умова циклу.
Читаємо помилки¶
Перш ніж виправляти код, прочитай останній рядок повідомлення про помилку: там тип помилки й пояснення. Три найчастіші в цьому уроці:
| Повідомлення | Що сталося | Як виправити |
|---|---|---|
SyntaxError: expected ':' |
після if, elif, else чи while забули двокрапку |
if order_total >= 500: |
IndentationError: expected an indented block after 'if' statement on line 1 |
блок під умовою без відступу | 4 пробіли перед кожним рядком блоку |
ValueError: invalid literal for int() with base 10: 'два' |
int() отримав текст, який не є цілим числом |
перевір .isdigit() перед int() |
TypeError: can only concatenate str (not "int") to str |
до рядка з input() додали число |
спершу int(...) |
Python показує й номер рядка, де знайшов помилку. Для SyntaxError справжня причина іноді стоїть рядком вище: наприклад, незакрита дужка.
import і аргументи командного рядка¶
Мінімальний import¶
Іноді програмі потрібна готова функція, якої немає серед вбудованих. Наприклад, випадкове число для гри в розділі «Спробуй самостійно»:
import random підключає модуль random — файл із готовими функціями для випадкових значень. Після цього до його функцій звертаються через крапку: random.randint(1, 20) повертає випадкове ціле число від 1 до 20, обидві межі включно. Як улаштовані модулі, розберемо в уроці 13.
sys.argv — слова після python файл.py¶
input() питає дані під час роботи програми. Є й другий спосіб — передати їх під час запуску, словами після імені файлу. Їх збирає модуль sys. Файл argv_demo.py:
Запуск у терміналі:
sys.argv[0]— ім'я самого файлу,sys.argv[1]— перше слово після нього, і так далі. Індексація — як у рядків з уроку 3.- Кожен елемент — рядок, навіть
'87'. Правило те саме, що дляinput(): перевір.isdigit(), потімint(). len(sys.argv)— скільки всього слів разом з іменем файлу. Квадратні дужки у виводі означають список — тема уроку 5.
Програма має перевірити, що аргумент один і що це число, — інакше пояснити, як її запускати. Файл grade.py перетворює бал на оцінку (A — від 90, B — від 75, C — від 60, інакше F):
import sys
if len(sys.argv) != 2:
print("Використання: python grade.py <бал>")
elif not sys.argv[1].isdigit():
print("Бал має бути цілим числом, отримано:", sys.argv[1])
else:
score = int(sys.argv[1])
if score > 100:
print("Бал не може бути більшим за 100")
elif score >= 90:
print("A")
elif score >= 75:
print("B")
elif score >= 60:
print("C")
else:
print("F")
$ python grade.py 87
B
$ python grade.py 120
Бал не може бути більшим за 100
$ python grade.py
Використання: python grade.py <бал>
$ python grade.py 87 90
Використання: python grade.py <бал>
$ python grade.py 8.5
Бал має бути цілим числом, отримано: 8.5
Зверни увагу на порядок: спершу перевіряємо кількість аргументів (інакше sys.argv[1] не існуватиме), потім — формат, і лише потім перетворюємо й працюємо з числом.
Рядок if __name__ == '__main__': — упізнай, коли побачиш
У чужих програмах основний код часто стоїть під умовою if __name__ == "__main__":. Вона означає: «виконати цей блок, лише коли файл запустили командою python файл.py, а не імпортували в іншу програму». Докладно — в уроці 13 про модулі; поки що досить знати, навіщо цей рядок.
Практика¶
Розібраний приклад: приймання замовлення¶
Зберемо все разом. Програма:
- приймає позиції меню (кава — 55 грн, круасан — 70 грн, чай — 40 грн), доки офіціант не введе
готово; - ігнорує регістр і пробіли, попереджає про невідомі позиції;
- наприкінці показує кількість позицій, суму і правило доставки.
Приклад сеансу (після Позиція: — те, що ввів офіціант):
Приймаємо замовлення. Команди: кава, круасан, чай, готово
Позиція: Кава
Додано: кава — 55 грн. Разом: 55 грн
Позиція: чай
Додано: чай — 40 грн. Разом: 95 грн
Позиція: піца
Такої позиції немає: піца
Позиція: круасан
Додано: круасан — 70 грн. Разом: 165 грн
Позиція: кава
Додано: кава — 55 грн. Разом: 220 грн
Позиція: готово
Позицій у замовленні: 4
Сума: 220 грн
Доставка: 60 грн
Що відбувається в ключових рядках:
- рядки 3–4 — початковий стан: сума й кількість позицій дорівнюють нулю;
- рядок 6 — цикл без умови виходу; вихід лише через
break; - рядок 7 — введення очищається, тому
"Кава"і" чай "розпізнаються; - рядок 10 —
breakна командіготово: цикл завершується, програма переходить до рядка 26; - рядки 12–20 — ланцюжок
if/elif/elseобирає ціну; для невідомої позиції спрацьовуєelse; - рядок 20 —
continueне дає додати до суми позицію, якої немає в меню; - рядки 22–23 — зміна стану: сума і кількість позицій ростуть лише для справжніх позицій;
- рядки 29–34 — рішення про доставку, яке ми розібрали на початку уроку.
Перевір граничний випадок: якщо одразу ввести готово, цикл завершиться на першому повторі, і програма виведе Сума: 0 грн та повідомлення про мінімальне замовлення.
Зміни приклад¶
Додай до програми дві зміни. Меню, ціни й решту поведінки залиш як є.
- Команда
рахунокпоказує поточний стан і не завершує приймання:Зараз у замовленні: 1 поз., 55 грн. - Команда
готовоне спрацьовує, поки замовлення порожнє: програма виводитьЗамовлення порожнє — додай хоча б одну позиціюі чекає далі.
Приклад 1. Введення: готово, кава, рахунок, чай, готово.
Позиція: готово
Замовлення порожнє — додай хоча б одну позицію
Позиція: кава
Додано: кава — 55 грн. Разом: 55 грн
Позиція: рахунок
Зараз у замовленні: 1 поз., 55 грн
Позиція: чай
Додано: чай — 40 грн. Разом: 95 грн
Позиція: готово
Позицій у замовленні: 2
Сума: 95 грн
Мінімальне замовлення для доставки — 200 грн
Приклад 2. Введення: рахунок, Рахунок, круасан, готово — рахунок працює і для порожнього замовлення, з пробілами й великою літерою.
Критерії перевірки:
рахунокне змінює суму і кількість позицій;- порожнє замовлення не завершується командою
готово; - непорожнє замовлення завершується з першої спроби;
- обидві команди розпізнаються незалежно від регістру й пробілів.
Підказка
Обидві нові перевірки — це ще дві гілки перед вибором ціни. Подумай, де потрібен continue, а де break, і яка змінна вже знає, чи порожнє замовлення.
Задачі з аргументами командного рядка¶
Кожна програма запускається з терміналу, отримує один аргумент і перевіряє його, як grade.py: неправильна кількість або не число — підказка Використання: ….
1. Зворотний відлік — countdown.py, цикл while:
2. Сума цифр — digits.py. Остання цифра числа — number % 10, відкинути її — number //= 10; повторювати, доки число не стане нулем:
Підказка до суми цифр
Для 2026: 2026 % 10 = 6, лишається 202; 202 % 10 = 2, лишається 20; 0, лишається 2; 2, лишається 0 — стоп. 6 + 2 + 0 + 2 = 10.
Розв'язки обох — у ноутбуці заняття.
Спробуй самостійно: «Вгадай число»¶
Інший контекст, ті самі інструменти. Програма загадує випадкове ціле число від 1 до 20 (random.randint), а гравець вгадує його щонайбільше за 5 спроб.
Вимоги:
- введення очищається через
.strip(); - якщо введено не ціле число або число поза межами 1–20, програма пише
Потрібне ціле число від 1 до 20, і спроба не рахується; - на кожну помилкову спробу програма підказує
БільшеабоМенше; - у разі вгадування програма пише
Вгадав за N спроб(и)!і завершується; - після 5 невдалих спроб:
Спроби закінчились. Було загадано: X.
Для перевірки тимчасово заміни випадкове число фіксованим: secret = 13.
Приклад сеансу з secret = 13:
Твоє число: 10
Більше
Твоє число: п'ять
Потрібне ціле число від 1 до 20
Твоє число: 15
Менше
Твоє число: 13
Вгадав за 3 спроб(и)!
Критерії перевірки:
13з першої спроби →Вгадав за 1 спроб(и)!;1,2,25,3,4,5→ п'ять підказокБільше, повідомлення про межі для25, потімСпроби закінчились. Було загадано: 13;п'ять,-3і порожній рядок не зменшують кількість спроб;- програма не падає з
ValueErrorна жодному введенні.
Підказка
Тобі знадобляться лічильник спроб, умова циклу на основі цього лічильника і змінна-прапорець guessed = False, яку ти зміниш на True перед break. Після циклу за нею можна вирішити, яке повідомлення вивести.
Розширення: спроби з аргументу і while … else. Зроби версію guess.py, де кількість спроб передається при запуску (python guess.py 3) і перевіряється, як у grade.py. Замість прапорця guessed використай while … else: break при вгадуванні, а гілка else друкує відповідь, коли спроби скінчились.
Розв'язок — у ноутбуці заняття.
Підсумок¶
| Що потрібно | Як записати |
|---|---|
| порівняти значення | ==, !=, <, <=, >, >= |
| перевірити діапазон | 200 <= order_total < 500 |
| обрати одну дію з кількох | if / elif / else — виконується перша істинна гілка |
| обрати дію за конкретним значенням | match + case "значення":, «усе інше» — case _: (Python 3.10+) |
| поєднати умови | and, or, not; дужки для ясності |
| перевірити, що значення немає | value is None |
| парне число | number % 2 == 0 |
| перевірити, що рядок непорожній | if text: |
| отримати дані | input() або sys.argv[1] → завжди str; .strip().lower(), .isdigit(), int() |
| повторювати, поки умова істинна | while умова: + зміна стану в блоці |
| вийти з циклу | break |
| перейти до наступної перевірки | continue (стан має змінитися до нього) |
дія, коли цикл завершився без break |
while … else |
Самоперевірка¶
- Що виведе
print(10 == 10.0, "10" == 10)? - Чому для правила доставки не можна починати з умови
order_total >= 200? - Що дає
bool("0")і чому? - Чим
if tip is None:відрізняється відif not tip:? - Скільки разів виконається блок
while count < 0:, якщоcount = 0? - Що робить
continueу цикліwhileі чим він небезпечний? - Що виведуть
print(1 < 3 > 2)іprint((1 < 3) > 2)? - Як Python розставить дужки у виразі
not guests > 10 and is_weekend? - Що лежить у
sys.argvпісля запускуpython grade.py 87і якого типуsys.argv[1]?
Відповіді
True False. Числа10і10.0рівні за значенням, а рядок і число — ні.- Будь-яка сума від 500 теж більша за 200, тому перша гілка перехопить її, і безкоштовна доставка стане недосяжною. Починай з найбільшої межі.
True: рядок"0"непорожній, а Python не аналізує його зміст.is Noneспрацює лише для відсутнього значення.not tipспрацює ще й для0та"", тобто сплутає «не вказано» з «вказано нуль».- Жодного:
0 < 0хибна вже на першій перевірці. - Пропускає решту поточного повтору і повертає до перевірки умови. Якщо зміна стану стоїть після
continue, вона не виконається, і цикл може стати нескінченним. TrueіFalse. Без дужок — ланцюжок1 < 3 and 3 > 2; з дужками —True > 2, тобто1 > 2.(not (guests > 10)) and is_weekend:notсильніший заand, а порівняння — сильніше заnot.['grade.py', '87'];sys.argv[1]— рядок'87', тому перед обчисленнями потрібні.isdigit()іint().
Що далі¶
- Ноутбук заняття з передбаченнями та вправами: Відкрити вправи в Colab Переглянути розв’язки
- Окремий ноутбук про
match/case: Відкрити вправи в Colab Переглянути розв’язки - Наступний урок: Урок 5. Списки, кортежі та множини. Досі замовлення зберігало лише суму й кількість. Щоб пам'ятати самі позиції, потрібні колекції.
Документація¶
- Туторіал:
if,match,breakіcontinue - Перевірка істинності (truth value testing), логічні операції
and,or,not, порівняння - Довідник мови: порівняння й ланцюжки, логічні операції, пріоритет операторів
- Інструкції:
if,match,while,break,continue - Функції й методи:
input(),int(),bool(),str.strip(),str.lower(),str.isdigit(),sys.argv,None,random.randint() - PEP 636 — Structural Pattern Matching: Tutorial, PEP 634 — специфікація
- Туторіал: More on Conditions — ланцюжки порівнянь і коротке обчислення;
elseу циклах