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

Пакети

Модулі можуть об'єднуватись в пакети. Пакети слугують як простори імен для модулів і спосіб їх структурування.

Будь-який пакет є модулем, але не кожен модуль є пакетом.

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

Для того, щоб каталог був пакетом, у ньому повинен знаходитись файл __init__.py. Він автоматично виконується при імпортуванні певного модуля і може містити певні дії для ініціалізації або бути порожнім. Приклад (структура файлів):

  • package (тека, пакет)
    • __init__.py (файл, модуль)
    • module1.py (файл, модуль)
    • module2.py (файл, модуль)

Всередині пакетів можуть бути не тільки модулі, а й інші пакети також:

  • package (тека, пакет)
    • __init__.py (файл, модуль)
    • module1.py (файл, модуль)
    • module2.py (файл, модуль)
    • subpackage (тека, пакет)
      • __init__.py (файл, модуль)
      • submodule1.py (файл, модуль)
      • submodule2.py (файл, модуль)

Приклади імпорту з пакетами:

import package.module # модуль з пакета необхідно імпортувати явно
import package.subpackage.module
from package import module
from package import item
from package.subpackage import module, item
from module import *

При використанні оператора

from package import item

item може бути пакетом, модулем або будь-яким ім'ям, описаним у пакеті.

При використанні оператора

import package.item

item повинен бути модулем або пакетом.

Для того, щоб можна було імпортувати усі імена пакета

from package.subpackage import *

пакет має описувати список __all__, котрий містить імена підпакетів і модулів.

Відносний імпорт

Крім абсолютних існують також відносні імпорти:

  • крапка вказує на поточний пакет (відповідає імені пакета, у якому викликається імпорт)
  • дві крапки — на батьківський і так далі

Ці ж символи можуть бути використані одразу ж перед іменем пакета або модуля і впливати на те, де інтерпретатор буде шукати його. Приклади:

from . import name
from .. import name
from .package import name
from ..package import name

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

Фасад

Задача модуля __init__.py полягає в ініціалізації пакета, тому не варто реалізовувати у ньому всю логіку. В цьому модулі варто робити:

  • нічого
  • оголосити глобальні для модуля змінні
  • огородити пакет фасадом

Фасад (Facade) — шаблон проектування, який дозволяє приховати складність системи шляхом зведення усіх можливих зовнішніх викликів до одного об'єкта, який делегує їх відповідним об'єктам системи.

В Python, у контексті пакетів, фасад — це імпорт імен з вкладених у пакет модулів і пакетів і визначення змінної __all__.

Розглянемо на прикладі. Припустимо маємо пакет з такою структурою:

  • package
    • __init__.py
    • module.py
    • subpackage
      • __init__.py
      • subpackage_module.py

Спочатку для пакета subpackage ми імпортуємо увесь вміст вкладеного модуля і вказуємо, що це буде вмістом цього пакета:

# файл package/subpackage/__init__.py
from .subpackage_module import *
__all__ = subpackage_module.__all__

Тепер аналогічно поступимо з пакетом package: імпортуємо усе з вкладеного модуля module, потім усе з вкладеного пакета subpacage, і оголосимо __all__ як конкатенацію аналогічних змінних двох його нащадків:

# файл package/__init__.py
from .module import *
from .subpackage import *
__all__ = module.__all__ + subpacage.__all__

Тобто ми як би абстрагували структуру пакета. Тепер необов'язково пам'ятати як називаються модулі, як у пакеті взагалі все організовано. Ми можемо використовувати пакет package приблизно так:

from package import some_entity

Плюси такого "фасаду":

  • не треба запам'ятовувати внутрішню структуру пакета і думати, з чим саме він працює: модулем чи пакетом. Наприклад, ми можемо не задумуватись як імпортувати функцію urlopen:
        from urllib import urlopen
        from urllib.request import urlopen
        from urllib.requests import urlopen
    
  • інтерфейс не залежить від деталей реалізації — можна переміщувати код між внутрішніми модулями і пакетами

Мінуси:

  • на завантаження і ініціалізацію витрачається багато часу
Back to top