Пакети у Python – це спосіб організації модулів в єдиному просторі імен, що дозволяє таким чином групувати пов’язану функціональність. Пакет у Python – це просто каталог, який містить спеціальний файл __init__.py, котрий виконується під час імпорту пакету, і може містити будь-який код на Python.
Варто зауважити, що починаючи з версії Python 3.3 запроваджено концепцію namespace packages, які не вимагають наявності файлу __init__.py. Однак використання __init__.py все ще є поширеною практикою, адже це дає змогу явно вказати, що директорія є пакетом, і надає місце для коду ініціалізації пакета.
Призначення файлу __init__.py
- Коли інтерпретатор Python зустрічає каталог, який містить файл __init__.py, він розглядає його як пакет, і дозволяє імпортувати модулі з цього пакету за допомогою крапкової нотації.
- Файли __init__.py потрібні для керування поведінкою імпорту пакета, що дозволяє уникнути конфліктів імен, і зберегти ваш код впорядкованим.
- Також файл __init__.py може використовуватись для конфігурації пакету. Це може включати визначення змінних рівня пакета або імпорт інших модулів, від яких залежить пакет.
Розглянемо деякі приклади використання файлу __init__.py.
Визначення змінних на рівні пакету
Уявімо, що є пакет mypackage, структура якого виглядає наступним чином:
mypackage/
│── __init__.py
│── module1.py
│── module2.pyУ файлі __init__.py ми можемо визначити змінну рівня пакета, наприклад:
# mypackage/__init__.py
VERSION = '1.0.0'Доступ до цієї змінної можна отримати використовуючи ім’я пакету та крапкову нотацію:
import mypackage
print(mypackage.VERSION) # 1.0.0Це звичайна практика для розробників бібліотек, коли вони включають версію пакета та деякі інші метадані, у так званий root пакета.
Імпорт модулів до простору імен пакета
Файл __init__.py можна використовувати для автоматичного імпорту модулів пакета в його простір імен. Повернемось до структури пакету з попереднього прикладу:
mypackage/
│── __init__.py
│── module1.py
│── module2.pyФайл mypackage/__init__.py:
from . import module1
from . import module2Вміст модуля mypackage/module1.py:
def function():
print('Function module1')Вміст модуля mypackage/module2.py:
class A:
def __init__(self):
print('An instance of the class A from module2')Використання пакета mypackage:
import mypackage
mypackage.module1.function()
# Function module1
obj = mypackage.module2.A()
# An instance of the class A from module2У цьому прикладі, всередині файлу __init__.py, за допомогою крапкової нотації в простір імен пакету mypackage імпортуються модулі module1 та module2. Це дозволяє отримати доступ до вмісту цих модулів через ім’я пакету (наприклад mypackage.module1).
Визначення функцій на рівні пакету
Також у файлі __init__.py можна визначити функцію рівня пакету. Доступ до такої функції можна отримати після імпорту пакета, за допомогою крапкової нотації (наприклад mypackage.function()).
Розглянемо приклад:
my_package/
│── __init__.py
│── module1.py
│── module2.pyВизначимо функцію у файлі __init__.py:
# mypackage/__init__.py
import datetime
def log_message(msg):
timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print(f'[{timestamp}] {msg}')Оскільки функція log_message() визначена в __init__.py, її можна викликати безпосередньо після імпорту пакета:
import mypackage
mypackage.log_message('Test message')
# [2025-03-29 15:41:51] Test messageКерування експортом з пакета
Файл __init__.py у Python можна використовувати для визначення модулів та об’єктів доступних при імпорті пакета. Це робиться за допомогою спеціальної змінної __all__, котра визначає список імен (функцій, класів, змінних), які будуть доступні при імпорті через синтаксис import *.
Приклад:
mypackage/
│── __init__.py
│── module1.py
│── module2.pyВміст модуля mypackage/module1.py:
def function1():
print('Function module1')Вміст модуля mypackage/module2.py:
def function2():
print('Function module2')Тепер у файлі __init__.py вказуємо імена для експорту:
# mypackage/__init__.py
from .module1 import function1
__all__ = ['function1']Використання імпорту з пакета:
from mypackage import *
function1() # Function module1
function2() # NameError: name 'function2' is not defined. Did you mean: 'function1'?Функція function2() недоступна, оскільки вона відсутня у списку __all__.
Також слід додати про файли __init__.py
Файли __init__.py можуть бути порожніми: можна не додавати ніякого коду до файлу __init__.py, якщо в цьому немає потреби. Порожній файл __init__.py так само позначає каталог як пакет, як і файл з будь-яким кодом Python.
Файл __init__.py можна використовувати для виконання різноманітної конфігурації пакета: окрім визначення змінних і функцій на рівні пакету, можна наприклад зробити ініціалізацію з’єднання з базою даних, завантажити конфігураційні дані, тощо.
Файли __init__.py можуть бути вкладеними: тобто якщо у пакеті є вкладені пакети, можна додати файл __init__.py до кожного з них, що дає змогу визначати змінні та функції навіть на рівні вкладених пакетів.
Починаючи з версії Python 3.3, немає обов’язкової потреби включати файл __init__.py до каталогів пакетів. Однак, для забезпечення сумісності з попередніми версіями Python, все ж рекомендується додавати його.