Ефективне налагодження за допомогою оператора Python Assert

Чи займаєтесь ви програмуванням? Якщо ваша відповідь “так”, то налагодження коду є невід’ємною частиною роботи, незалежно від мови програмування, яку ви використовуєте. У цій статті ми розглянемо, як оператор `assert` у Python може стати вашим потужним інструментом у процесі налагодження.

Розробляючи програмний проєкт, ви неминуче зіткнетеся з необхідністю поділу його на окремі модулі: функції, класи тощо. На жаль, помилки та неочікувані результати через недоліки в реалізації є звичайною справою. Саме тут на допомогу приходять оператори `assert`.

У цьому посібнику ми детально вивчимо синтаксис використання оператора `assert`, а потім розглянемо кілька практичних прикладів, щоб побачити його в дії. Ми також обговоримо, що таке помилки `AssertionError` і як вони можуть допомогти виявити та виправити помилки в процесі розробки.

Отже, почнемо!

Як використовувати оператор Assert в Python

Спочатку розглянемо синтаксис використання оператора `assert`, а потім перейдемо до практичних прикладів кодування.

Синтаксис оператора Assert

Розглянемо базовий синтаксис оператора `assert` у Python:

assert вираз, повідомлення

Тут:

  • `вираз` – це будь-який валідний вираз Python, який необхідно оцінити. Це може бути умова, що перевіряє значення змінної, істинність змінної, значення, що повертається функцією тощо.
  • Доки `вираз` оцінюється як `True`, оператор `assert` не генерує помилку та не повертає жодного значення. Це означає, що програма працює коректно.
  • Якщо `вираз` стає `False`, виникає виняток `AssertionError`.
  • `повідомлення` – це необов’язковий рядок. Ви можете надати повідомлення, яке буде відображатися у трасуванні стека кожного разу, коли виникає виняток `AssertionError`.

Тепер перейдемо до кількох практичних прикладів, де оператор `assert` допоможе нам написати більш надійний код.

Приклади коду, що використовуються в цьому посібнику, можна знайти в цьому репозиторії на GitHub.

Приклади використання оператора Assert в Python

Розглянемо такий сценарій. У вашому коді є змінна, що представляє знижку. Ви хочете переконатися, що її значення завжди менше або дорівнює максимальній допустимій знижці `max_discount`.

Щоб уникнути випадкового встановлення значення змінної `discount`, що перевищує заданий ліміт, ви можете скористатися `assert`. Вираз для оцінки буде таким: `знижка <= максимальна_знижка`.

>>> max_discount = 50
>>> discount = 20
>>> assert discount <= max_discount

Тут `discount` (20) менше, ніж `max_discount` (50), тому оператор `assert` не видає помилки.

Виняток AssertionError

Якщо значення `discount` перевищить `max_discount`, виникне виняток `AssertionError`.

>>> discount = 75
>>> assert discount <= max_discount
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

Як ми знаємо, оператор `assert` також дозволяє нам вказати додаткове повідомлення.

Використаємо f-рядок для надання більш інформативного діагностичного повідомлення, що включатиме значення `discount` і `max_discount`.

>>> assert discount <= max_discount, f"Знижка повинна бути не більше {max_discount}; отримано discount = {discount}"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: Знижка повинна бути не більше 50; отримано discount = 75

Як показано вище, виняток `AssertionError` тепер містить значення змінних `discount` і `max_discount`.

Налагодження та тестування функцій Python за допомогою Assert

Під час створення функцій ви можете випадково допустити помилки (логічні помилки), які завадять вашій функції працювати належним чином.

Розглянемо наступний приклад. Припустимо, що учні пишуть контрольну роботу, і їм надається можливість відповісти на бонусне питання. За правильну відповідь на бонусне питання вони отримають додаткові 10 балів. 😄

Розглянемо функцію `get_final_score`:

  • Вона приймає поточний бал, значення логічної змінної, що вказує на те, чи було бонусне питання вирішено.
  • Якщо учень відповів на бонусне питання, логічна змінна `bonus` має значення `True`, і він отримує на 10 балів більше до свого поточного результату.
  • Потім функція повертає остаточну оцінку.
def get_final_score(score, bonus):
    if bonus:
        score += 10
    return score

Зробимо кілька викликів цієї функції. Для балів 34 і 40 із `bonus` встановленим у `True` і `False`, остаточні бали становитимуть 44 і 40 відповідно.

print(get_final_score(34, True))
# 44
print(get_final_score(40, False))
# 40

Проте, скажімо, максимальна кількість балів за контрольну роботу становить 50. Тож, якщо учень набрав 49 балів і також відповів на бонусне питання, функція `get_final_score` із задоволенням обчислить остаточний бал 59.

print(get_final_score(49, True))
# 59

Технічно це можливо. Але припустимо, що учень не може набрати більше максимально можливого балу за контрольну роботу. 🙂

Отже, ініціалізуємо змінну `max_score`. Запишемо результат, який повертає функція у змінну `final_score`.

Потім ми додамо твердження, яке перевіряє, чи є `final_score` меншим або рівним `max_score`.

def get_final_score(score, bonus):
    if bonus:
        score += 10
    return score

final_score = get_final_score(47, True)
max_score = 50

assert final_score <= max_score

Тепер ми отримаємо виняток `AssertionError` для виклику функції `get_final_score(47, True)`:

Traceback (most recent call last):
  File "main.py", line 17, in <module>
    assert final_score <= max_score
AssertionError

Тепер додамо більш описовий f-рядок до оператора `assert`:

assert final_score <= max_score, f"final_score повинен бути не більше {max_score}; отримано {final_score}"
Traceback (most recent call last):
  File "main.py", line 17, in <module>
    assert final_score <= max_score,f"final_score повинен бути не більше {max_score}; отримано {final_score}"
AssertionError: final_score повинен бути не більше 50; отримано 57

Модифікація функції

Повернемося до визначення функції `get_final_score`, щоб виправити несподівану поведінку:

  • Функція `get_final_score` тепер також приймає `max_score` як параметр.
  • Ми перевіряємо, чи `bonus` дорівнює `True`. Якщо так, додаємо 10 балів до змінної `score`.
  • Потім ми перевіряємо, чи `score` більше за `max_score`. Якщо так, повертаємо `max_score`.
  • В іншому випадку повертаємо `score`.

Тепер ми впевнилися, що кінцевий результат завжди менший або дорівнює `max_score`.

def get_final_score(score, bonus, max_score):
    if bonus:
        score += 10
    if score > max_score:
        return max_score
    return score

Для швидкої практики, напишіть кілька тверджень, щоб підтвердити, що функція тепер працює коректно.

Примітка про виняток AssertionError

Хоча виняток `AssertionError` виникає, коли вираз отримує значення `False`, важливо пам’ятати, що не слід обробляти такі помилки, як звичайні винятки. Це означає, що ми не повинні робити щось подібне:

try:
    <виконання коду>
except AssertionError:
    <обробка помилки>

У попередньому прикладі `get_final_score` ми використовували твердження, щоб перевірити, чи є `final_score` менше за `max_score`. Потім ми змінили визначення функції так, щоб помилка твердження не виникала.

Саме для цього і потрібні твердження. Це інструмент перевірки коректності коду та допомога в написанні якіснішого коду. Обробка винятків, з іншого боку, призначена для передбачення та обробки несподіваних помилок під час виконання. Це часто стосується недійсних типів введення та значень.

Отже, підсумовуючи, використовуйте оператор `assert` в Python для ефективного налагодження і не обробляйте `AssertionErrors` як винятки.

Висновок

Цей посібник допоміг вам зрозуміти, як використовувати оператор `assert` в Python. Ось основні висновки:

  • Інструкція `assert` у Python має форму `assert вираз`. Вона перевіряє, чи є вираз істинним. Якщо він не є `True`, виникає виняток `AssertionError`.
  • Ви також можете використовувати `assert` із синтаксисом `assert вираз, повідомлення`. Це виведе рядок повідомлення кожного разу, коли станеться виняток `AssertionError`.
  • Слід пам’ятати, що обробка винятків не повинна використовуватися для обробки помилок твердження. Використовуйте твердження як корисний інструмент налагодження для перевірки працездатності вашого коду.

Як розробнику, твердження допоможуть вам у процесі налагодження. Щоб переконатися, що всі окремі компоненти (модулі) проєкту працюють належним чином, вам варто навчитися писати модульні тести на Python.

Далі перегляньте цей список проєктів Python для початківців, над якими ви можете попрацювати.