Перейти до змісту

Урок 18. Узагальнення модуля 1 і фінальний проєкт

Модуль 1 мав дві історії. Кафе «Смачно»: від змінних з сумою чека (урок 3) до звіту з функцій (урок 8), модулів і командного рядка (урок 13), винятків (урок 14), файлів і JSON (урок 15) та командного проєкту на GitHub (урок 16). Диспетчерська таксі: три практикуми про ціну рішення (урок 9), вибір стратегії пошуку (урок 12) і хеш-структури (урок 17).

А тепер згадай весну 2020-го. Карантин: зали кафе зачинені, гостей немає. Таксі стоять: пасажирів теж немає. І водночас — бум доставки: люди сидять удома й замовляють їжу, а бізнес, який швидко переналаштувався, вижив. Кафе «Смачно» і диспетчерська таксі домовляються: кафе готує, водії таксі розвозять. За кілька тижнів доставка стає головним джерелом виторгу для обох.

Але облік у них різний: у кафе — рядки каси, у таксі — журнал поїздок. Потрібна одна система, яка знає і про чеки, і про доставки. Це фінальний проєкт модуля: ти збереш його сам, за день чи два, і в ньому знадобиться майже все, що було в модулі.

Що потрібно: уроки 1–17.

Після уроку ти зможеш:

  • пояснити, як теми модуля пов'язані між собою і навіщо кожна з них знадобилась;
  • проєктувати програму зверху вниз: задача → дані → стан → операції → перетворення → модулі → код;
  • зібрати консольну систему з шести модулів, яка зберігає стан у JSON, пояснює помилки людською мовою і перевіряється тестами;
  • вести проєкт у власному репозиторії: гілки, коміти, Pull Request, README.

Задача розділу. Фінальний проєкт «Смачно + Таксі» у теці capstone/.

Ноутбук заняття: Відкрити вправи в Colab Переглянути розв’язки

Карта модуля

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;

    A["основа · уроки 1–4<br>середовище, типи, умови"] --> B["дані · уроки 5–6<br>list, tuple, set, dict"]
    B --> C["функції · уроки 8, 10, 11<br>return, декоратори, генератори"]
    C --> D["система · уроки 13–16<br>модулі, винятки, файли, Git"]
    B --> E["алгоритми · уроки 9, 12, 17<br>Big O, пошук, хешування"]
    D --> F["фінальний проєкт<br>Смачно + Таксі"]
    E --> F

    class A,B,C,D,E step
    class F success
Урок Що дав Де знадобиться в проєкті
3–4 типи, перетворення, умови float("540.00"), перевірки сум і кількості гостей
5–6 NamedTuple, списки, словники Order, Delivery, лічильники
7 функції з контрактом кожна частина — набір маленьких функцій
8, 11, 16 Big O, пошук, dict / set / Counter пошук замовлення за номером, райони, ваучер
12 модулі, datetime, sys.argv шість файлів, дати, команди з термінала
13 винятки, raise зрозумілі помилки замість трасувань
14 with open, JSON стан у data.json між запусками
15 Git, гілки, PR, тести власний репозиторій, check.py

Десять ідей модуля

Коротко — те, що варто пам'ятати й через рік:

  1. Змінна — ярлик, а не коробка (урок 3). b = a для списку — два ярлики на один об'єкт.
  2. Python динамічний, але строгий (урок 3). "Чек: " + 540 — TypeError: перетворюй явно.
  3. Структура даних визначає алгоритм (уроки 9, 12, 17). Той самий пошук — O(n) у списку, O(log n) у відсортованому, O(1) у словнику.
  4. Функція — рівень абстракції (урок 8). print_report(orders) читається як крок, а не як 30 рядків.
  5. Чиста функція передбачувана (урок 8). Той самий вхід — той самий результат, без змін чужих даних.
  6. Помилки — це нормально, якщо їх обробляти (урок 14). Ловити там, де знаєш, що відповісти людині, і ніколи не мовчки.
  7. Дані живуть поза програмою (урок 15). Змінні зникають, файли лишаються.
  8. Стандартна бібліотека — перший ресурс (урок 13). datetime, collections, json, csv — уже встановлені.
  9. Git — спосіб мислення (урок 16). Маленькі осмислені кроки, історія, гілки, рев'ю.
  10. Середовище — фундамент (урок 2). Активуй середовище → встанови → запускай.

Перевір себе: прогнози

Спершу скажи, що надрукує кожен фрагмент, потім розгорни відповідь. У ноутбуці заняття ці самі прогнози можна запустити.

1. Урок 3:

a = [540, 320]
b = a
b.append(980)
print(a)

2. Урок 4:

print(bool("0"), bool(0), bool([]))

3. Урок 6:

counts = {}
for day in ["пт", "сб", "пт"]:
    counts[day] = counts.get(day, 0) + 1
print(counts)

4. Урок 8:

def add_order(bill, orders=[]):
    orders.append(bill)
    return orders


add_order(540)
print(add_order(320))

5. Урок 11:

doubled = (bill * 2 for bill in [100, 200])
print(list(doubled), list(doubled))

6. Урок 14:

try:
    int("7.5")
except ValueError:
    print("A")
else:
    print("B")
finally:
    print("C")

7. Урок 15:

import json

print(json.loads(json.dumps({7: ("пт", 540)})))

8. Урок 17:

routes = {}
routes[["Поділ", "Оболонь"]] = 1
Відповіді
  1. [540, 320, 980] — a і b — ярлики одного списку.
  2. True False False — непорожній рядок істинний, навіть "0"; нуль і порожній список — хибні.
  3. {'пт': 2, 'сб': 1}.
  4. [540, 320] — список за замовчуванням створюється один раз, при оголошенні функції. Правильно: orders=None і if orders is None: orders = [].
  5. [200, 400] [] — генератор вичерпується після першого проходу.
  6. A, потім C: else лише без винятку, finally — завжди.
  7. {'7': ['пт', 540]} — ключ став рядком, кортеж — списком.
  8. TypeError: unhashable type: 'list' — ключ має бути незмінним; маршрут — кортежем.
Чекліст модуля 1: чи можу я пояснити…
  • різницю між інтерпретатором, pip, venv і IDE; навіщо активувати середовище;
  • чим int, float, str, bool відрізняються і як їх перетворювати; чому 0.1 + 0.2 != 0.3;
  • порядок if / elif / else і коли while зупиняється;
  • коли брати list, tuple, set, dict; як розпакувати кортеж;
  • return проти print; значення за замовчуванням; область видимості;
  • що робить декоратор і functools.wraps; чим генератор відрізняється від списку;
  • що таке O(1), O(log n), O(n), O(n²) і як їх упізнати в коді;
  • що робить import; навіщо if __name__ == "__main__":;
  • try / except / else / finally, raise, чому не можна голий except:;
  • with open(...), режими r / w / a, json.dump / json.load;
  • git add / commit / push, гілка, Pull Request, конфлікт.

Якщо якийсь пункт не пояснюється — повернися до уроку з карти вище, перш ніж братися за проєкт.

Фінальний проєкт: Смачно + Таксі

Доставка на карантині працює так. Каса кафе, як і раніше, дає замовлення рядками. Адміністратор додає до замовлення доставку: район, вартість, водія. Власниця хоче спільний звіт: скільки замовлень, скільки з них доставлено, виторг кафе й таксі, найкращий день, популярні райони — куди варто відправити більше водіїв. А служба підтримки — знайти пару доставок під ваучер.

Великі програми не пишуть «з першого рядка коду». Їх проєктують зверху вниз, і лише потім пишуть код. Пройдемо ці кроки для нашої системи.

1. Задача — одним реченням

Консольна програма, яка зберігає замовлення кафе й доставки таксі між запусками і відповідає на питання власниці, адміністратора й служби підтримки.

2. Дані — які записи існують

Запис Поля Звідки взявся
Order id, time (datetime), bill, tip, guests каса, уроки 14–15
Delivery order_id, district, fare, driver таксі, уроки 9, 12, 17

Зв'язок між ними — Delivery.order_id: номер замовлення, яке доставляють. У кожного замовлення щонайбільше одна доставка.

3. Стан — що треба пам'ятати між запусками

Два списки, orders і deliveries. Між запусками вони живуть у data.json (урок 15): кожна команда спершу читає стан, потім, якщо щось змінила, записує його назад. Перший запуск — файлу ще немає, стан порожній.

4. Операції — що вміє система

Команда Що робить Змінює стан?
import ФАЙЛ усі правильні рядки каси — у замовлення, про зіпсовані пояснення так
add-order "РЯДОК" одне замовлення в тому самому форматі так
add-delivery НОМЕР РАЙОН ВАРТІСТЬ ВОДІЙ доставка існуючого замовлення так
find НОМЕР замовлення та його доставка ні
report спільний звіт кафе й таксі ні
voucher СУМА дві доставки на суму ваучера ні

5. Перетворення — як дані змінюють форму

рядок каси "2024-07-19 18:30;540.00;50;2"  →  parse_order   →  Order
аргументи ["1", "Оболонь", "230", "D-3"]    →  parse_delivery →  Delivery
Order / Delivery                           →  save_state    →  JSON  →  load_state  →  Order / Delivery
orders + deliveries                        →  cafe_stats, taxi-функції  →  текст звіту

Будь-які неправильні дані зупиняються на вході й перетворюються на ValueError з поясненням (урок 14). Далі в системі живуть лише правильні Order і Delivery.

6. Модулі — як розділити систему

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;

    APP["app.py<br>команди з термінала"] --> CMD["6. commands.py<br>зв'язує кафе і таксі"]
    APP --> ST["3. storage.py<br>data.json"]
    CMD --> PR["2. parsing.py<br>перевірка входу"]
    CMD --> CAFE["4. cafe.py<br>статистика кафе"]
    CMD --> TAXI["5. taxi.py<br>індекс, райони, ваучер"]
    CAFE --> RU["1. rules.py<br>день і прийом їжі"]
    CMD --> RU
    PR --> MOD["models.py<br>Order, Delivery"]
    ST --> MOD

    class APP,MOD warning
    class CMD success
    class ST,PR,CAFE,TAXI,RU step

Помаранчеві модулі дає викладач: models.py з двома записами і app.py, який розбирає sys.argv, читає й записує стан, ловить ValueError і друкує Помилка: …. Шість сірих і зелений — твої. Контракт кожної функції — у її докстрінгу, як у командному проєкті уроку 16.

№ Файл Функції Урок
1 rules.py meal_type, day_name 12
2 parsing.py parse_order, parse_delivery 13
3 storage.py save_state, load_state 14
4 cafe.py cafe_stats 6, 7, 12
5 taxi.py delivery_index, top_districts, voucher_pair 11, 16
6 commands.py add_order, import_kasa, add_delivery, find_order, report, voucher 7, 13

commands.py — місце, де кафе і таксі стають однією системою. Наприклад, add_delivery перевіряє, що замовлення існує (множина номерів, урок 17) і ще не має доставки (словник-індекс), і лише потім додає доставку.

7. Код — і як перевірити

Коли все зроблено, сесія адміністратора виглядає так (справжній вивід еталонного розв'язку):

python app.py import kasa_2024_07.txt
Імпортовано: 4, пропущено: 5
  рядок 3: could not convert string to float: '540,00'
  рядок 5: очікували 4 поля, а маємо 2
  рядок 6: day is out of range for month
  рядок 7: сума чека має бути більшою за 0, а маємо -120.0
  рядок 8: гостей має бути хоча б один, а маємо 0
python app.py add-delivery 1 Оболонь 230 D-3
python app.py add-delivery 2 Поділ 180 D-1
python app.py add-delivery 3 Оболонь 270 D-3
python app.py add-delivery 3 Печерськ 150 D-2
Доставка №1: Оболонь, 230 грн, водій D-3
Доставка №2: Поділ, 180 грн, водій D-1
Доставка №3: Оболонь, 270 грн, водій D-3
Помилка: замовлення №3 вже має доставку
python app.py find 3
python app.py find три
№3 2024-07-20 20:15, 980.00 грн, гостей: 4, доставка: Оболонь (270 грн)
Помилка: номер замовлення має бути цілим числом, а маємо три
python app.py report
python app.py voucher 500
Замовлень: 4, з доставкою: 3
Кафе: 3040.00 грн, середній чек 760.00 грн, найкращий день: нд
Таксі: 680 грн за доставки
Райони: Оболонь 2, Поділ 1
Разом: 3720.00 грн
Ваучер 500 грн: доставки замовлень №1 і №3

Кожна команда — окремий запуск програми, але report бачить доставки, додані раніше: стан пережив завершення програми в data.json.

Перевірка — check.py, як в уроці 16:

python check.py        # таблиця: яка частина готова
python check.py 2      # тести частини 2
python check.py all    # усі частини + ця сесія адміністратора

Частини роби по порядку: 4 використовує 1, а 6 — майже все. Якщо тест частини 6 пише ще не зроблено — частина 2: напиши parse_order(), спершу допиши частину 2.

Як здавати

  1. Власний публічний репозиторій на GitHub (інструкція), у ньому вміст теки capstone/ без solution/.
  2. Кожна частина — окрема гілка part-N-…, осмислені коміти, Pull Request у свій main, злиття. Так історія покаже, як система росла.
  3. README.md твого репозиторію: що вміє система, як запустити, приклад виводу report.
  4. Посилання на репозиторій — викладачеві.

Можна командою, як в уроці 16: шість частин — шість людей, контракти ті самі.

Критерії оцінювання:

Критерій Як перевіряється
система працює python check.py all → ✅ Система працює!
помилки пояснені неправильні команди дають Помилка: …, а не трасування
стан зберігається data.json після перезапуску; data.json — у .gitignore
історія гілка й PR на кожну частину, зрозумілі повідомлення комітів
README опис, запуск, приклад виводу

Для охочих — розширення, яких немає в тестах:

  • команда list — усі замовлення в таблиці через f-string з вирівнюванням (урок 3);
  • drivers — виторг кожного водія, від більшого до меншого (Counter);
  • export report.json — звіт у JSON для сайту кафе (урок 15);
  • доставки за датою: report 2024-07-20 — лише замовлення цього дня.

Підсумок

Крок проєктування Питання У проєкті
Задача що і для кого? система доставки для кафе й таксі
Дані які записи? Order, Delivery
Стан що пам'ятати? два списки в data.json
Операції що вміє? шість команд
Перетворення як дані змінюють форму? рядок → запис → JSON → звіт
Модулі як розділити? шість модулів з контрактами
Код і перевірка чи працює? check.py all, Git, README

Що далі

  • Ноутбук заняття: Відкрити вправи в Colab Переглянути розв’язки — прогнози з усього модуля і запуск фінального проєкту в Colab.
  • Довідник: notes_module1_review.ipynb Open In Colab — докладний конспект усього модуля: велика картина, ментальна модель Python, чекліст. Нумерація уроків у ньому не збігається з нумерацією книги.
  • Модуль 2 починається з уроку 19 «Функції як об'єкти першого класу»: функції, які приймають і повертають функції — те, що вже трапилося в декораторах уроку 10, стане основним інструментом.

Документація і джерела