Пакети у 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, все ж рекомендується додавати його.


Мітки: