Розуміння того, якщо __name__ == ‘__main__’ у Python

У цій статті ми розглянемо, як функціонує і яке значення має вираз if __name__ == '__main__' у мові програмування Python.

Чи доводилося вам переглядати кодові бази, написані на Python, з великою кількістю модулів?

Якщо так, то ви, напевне, неодноразово стикалися з умовним оператором if __name__ == '__main__' у різних модулях. Найближчими хвилинами ми розберемо, що означає ця умова і розглянемо випадки, коли вона може бути корисною.

Розпочнімо!

Яке значення має __name__ в Python?

У Python модуль – це файл з розширенням .py, що містить визначення функцій, вирази для обчислень і тому подібне. Наприклад, файл з назвою hello_world.py є модулем hello_world.

Коли ви запускаєте Python-модуль, інтерпретатор Python перед виконанням встановлює значення деяких спеціальних змінних. __name__ – одна з них. Ключ до розуміння __name__ полягає у розумінні того, як імпорт працює в Python.

📁 Завантажити вихідний код для цього розділу можна тут.

Перейдіть до папки example-1. Там ви знайдете файл module1.py. Змінна __name__ існує у просторі імен поточного модуля.

Цей модуль виводить на екран рядок, а потім значення змінної __name__.

# example-1/module1.py
print("Це модуль 1.")
print(f"Значення змінної __name__ в модулі 1: {__name__}.")

Тепер запустимо module1 з командного рядка.

$ python module1.py

У виведених даних ми побачимо, що значення змінної __name__ дорівнює __main__.

Це модуль 1.
Значення змінної __name__ в модулі 1: __main__.

Імпортування модулів у Python

Окрім безпосереднього запуску Python-модуля, інколи ви можете використовувати можливості іншого модуля Python всередині поточного. Python робить це можливим за допомогою імпортування.

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

Файл module2.py містить наступне. Ми імпортували module1 всередину module2.

# example-1/module2.py

import module1 # Імпортуємо module1

print(f"Це модуль 2")
print(f"Значення змінної __name__ в модулі 2: {__name__}.")

Тепер запустимо module2.py та подивимось на результат.

$ python module2.py

У виведених даних бачимо наступне:

  • module1 виконується, коли ми його імпортуємо в module2 і виводиться відповідний результат.
  • Але цього разу значення змінної __name__ не __main__, а module1.
  • Оскільки ми запускали module2 безпосередньо, то значення __name__ для цього модуля тепер __main__.
Вивід

Це модуль 1.
Значення змінної __name__ в модулі 1: module1.
Це модуль 2
Значення змінної __name__ в модулі 2: __main__.

💡 Ключова ідея:

– Якщо модуль запускається безпосередньо, значення його змінної __name__ дорівнює __main__.

– Якщо модуль імпортується в інший модуль, то значення його __name__ стає ім’ям модуля.

Приклад if __name__ == '__main__' в Python

У цьому розділі ми розглянемо приклад практичного застосування умовного оператора if __name__ == '__main__'. Ми визначимо просту функцію, а потім напишемо модульні тести, щоб перевірити, чи вона працює правильно.

📁 Завантажити код і слідкуйте за прикладом.

Вихідний код для цього розділу знаходиться у папці example-2.

Файл add.py – це Python-файл, який містить визначення функції add_ab(). Функція add_ab() приймає два числа і повертає їхню суму.

# example-2/add.py

def add_ab(a, b):
    return a + b

Для перевірки функції add_ab() ми використаємо модуль unittest, вбудований в Python.

Створення тестів для Python-функції

Погляньте на наступний фрагмент коду, який містить вміст модуля test_add.

# example-2/test_add.py

import unittest
from add import add_ab

class TestAdd(unittest.TestCase):
    def test_add_23(self):
        self.assertEqual(add_ab(2, 3), 5)

    def test_add_19(self):
        self.assertEqual(add_ab(1, 9), 10)

    def test_add_1_minus7(self):
        self.assertEqual(add_ab(1, -7), -6)

Наведений вище код робить наступне:

  • Імпортує вбудований модуль Python unittest
  • Імпортує функцію add_ab() з модуля add
  • Визначає клас тестування TestAdd та набір тестових випадків як методи всередині цього класу

Щоб налаштувати модульні тести для вашого коду, спочатку потрібно створити тестовий клас, що є спадкоємцем класу unittest.TestCase. Всі тестові випадки мають бути визначені як методи в цьому класі, їхні імена мають починатися з test_.

Примітка: якщо ви не назвете методи як test_<ім'я_методу>, то відповідні тести не будуть виявлені і не будуть виконуватися.

Тепер спробуємо запустити модуль test_add з терміналу.

$ python test_add.py

Ви побачите, що виводу немає і жоден з тестів не був виконаний.

Чому так?🤔

Це тому, що для запуску модульних тестів потрібно запустити unittest як головний модуль при запуску test_add.py, використовуючи таку команду:

$ python -m unittest test_add.py

Після виконання цієї команди ми побачимо, що всі три тести пройшли успішно.

Вивід
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

Але чи не було б зручніше запускати тести при звичайному запуску модуля test_add? Дізнаємось, як це зробити, у наступному розділі.

Використання if __name__ == '__main__' для запуску unittest як головного модуля

Якщо ви хочете запустити всі модульні тести при безпосередньому запуску модуля, ви можете додати умовний оператор.

# example-2/test_add.py

import unittest
from add import add_ab

class TestAdd(unittest.TestCase):
    def test_add_23(self):
        self.assertEqual(add_ab(2, 3), 5)

    def test_add_19(self):
        self.assertEqual(add_ab(1, 9), 10)

    def test_add_1_minus7(self):
        self.assertEqual(add_ab(1, -7), -6)

# Запустити unittest як головний модуль
if __name__ == '__main__':
    unittest.main()

Умовний оператор у наведеному вище коді каже інтерпретатору Python: якщо цей модуль запускається безпосередньо, то запусти код всередині unittest.main().

Після додавання цих двох рядків коду, ви можете запустити модуль test_add.

$ python test_add.py

▶️ Безпосередній запуск модуля тестування запускає усі три тести, які ми визначили.

Вивід
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

Результат OK означає, що всі тести виконано успішно. Три крапки ... вказують на те, що було виконано три тести і всі вони пройшли.

Тепер змінимо очікуване значення, що повертає test_add_1_minus7 на 8. Оскільки в цьому випадку функція повертає -6, один тест має завершитись з помилкою.

def test_add_1_minus7(self):
    self.assertEqual(add_ab(1, -7), 8)

Як видно з виводу нижче, ми отримали .F.: з трьох тестів один (другий) завершився помилкою. Також в трасуванні є AssertionError, що показує -6 != 8.

Вивід
.F.
======================================================================
FAIL: test_add_1_minus7 (__main__.TestAdd)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_add.py", line 12, in test_add_1_minus7
    self.assertEqual(add_ab(1, -7), 8)
AssertionError: -6 != 8

----------------------------------------------------------------------
Ran 3 tests in 0.021s

FAILED (failures=1)

Важливо зауважити, що тести не обов’язково виконуються в тому порядку, в якому вони визначені у тестовому класі. У наведеному вище прикладі test_add_1_minus7 визначено третім методом в класі, але відповідний тест був виконаний другим.

Підсумки

Сподіваюсь, цей посібник допоміг вам зрозуміти, як працює умовний оператор if __name__ == '__main__' в Python.

Короткий підсумок основних висновків:

  • Інтерпретатор Python встановлює змінну __name__ перед виконанням Python-скрипту.
  • Коли ви запускаєте модуль безпосередньо, значення __name__ дорівнює __main__.
  • Коли ви імпортуєте модуль в інший Python-скрипт, значення __name__ стає ім’ям модуля.
  • Ви можете використовувати if __name__ == '__main__' для управління виконанням та визначення, які частини модуля запускаються при прямому запуску і при імпортуванні відповідно.

Далі перегляньте цей докладний посібник про множини в Python. Бажаю успіхів у навчанні!🎉