Что такое пакет python
Перейти к содержимому

Что такое пакет python

  • автор:

Пакеты модулей в Python

По мере роста количества модулей становится трудно их отслеживать. Это становиться заметно, если они имеют похожие имена или функциональность и хотелось бы иметь какое то средство их группировки и организации.

Пакеты позволяют иерархически структурировать пространство имен модуля с использованием точечной нотации. Точно так же, как модули помогают избежать коллизий между именами глобальных переменных, пакеты помогают избежать коллизий между именами модулей.

Можно рассматривать пакеты как каталоги в файловой системе, а модули как файлы в каталогах, но не надо воспринимать эту аналогию слишком буквально, поскольку пакеты и модули не обязательно должны происходить из файловой системы. Для целей обучения будем использовать эту удобную аналогию каталогов и файлов. Как и каталоги файловой системы, пакеты организованы иерархически, и сами пакеты могут содержать вложенные пакеты, а также обычные модули.

Важно помнить, что все пакеты являются модулями, но не все модули являются пакетами. Или, другими словами, пакеты — это просто особый вид модуля. В частности, любой модуль, содержащий атрибут __path__ считается пакетом.

Например есть каталог pkg с двумя модулями mod1.py и mod2.py . Допустим, что содержимое модулей имеет следующий код:

def foo(): print('Модуль 1, функция foo()') 
def bar(): print('Модуль 2, функция bar()') 

Если каталог pkg находится в одном из каталогов, содержащихся в выводе sys.path , то можно импортировать приведенные выше модули, использовав точечную нотацию pkg.mod1 , pkg.mod2 .

Использование конструкции импорта import [, . ] :

>>> import pkg.mod1, pkg.mod2 >>> pkg.mod1.foo() # Модуль 1, функция foo() >>> x = pkg.mod2.bar() >>> x # Модуль 2, функция bar() 

Использование конструкции импорта from import :

>>> from pkg.mod1 import foo >>> foo() # Модуль 1, функция foo() 
>>> from pkg.mod2 import bar as func_bar >>> x = func_bar() >>> x # Модуль 2, функция bar() 

Модули пакета можно импортировать и так — from package import module :

>>> from pkg import mod1 >>> mod1.foo() # Модуль 1, функция foo() >>> from pkg import mod2 as two_mod >>> two_mod.bar() # Модуль 2, функция bar() 

Технически также можно импортировать пакет import pkg . Это синтаксически правильный импорт Python, но он не помещает ни один из модулей пакета pkg в локальное пространство имен скрипта:

>>> import pkg >>> pkg # >>> pkg.mod1 # Traceback (most recent call last): # . # AttributeError: module 'pkg' has no attribute 'mod1' >>> pkg.mod2.bar() # Traceback (most recent call last): # . # AttributeError: module 'pkg' has no attribute 'mod2' 

Чтобы фактически импортировать модули или их содержимое, вам нужно использовать одну из форм, показанных выше.

  • ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
  • Спецификация инструкции import
  • Определение модуля и его импорт
  • Конструкция импорта import modulle as name
  • Конструкция импорта from modulle import names
  • Конструкция импорта from modulle import name as alt_name
  • Как Python ищет импортируемый модуль
  • Список имен, определенных в модуле Python
  • Выполнение модуля как скрипта
  • Перезагрузка модуля
  • Пакеты модулей
  • Файл пакета __init__.py
  • Переменная __all__ в пакетах и модулях
  • Переменная пакета __path__
  • Относительный импорт пакетов
  • Вложенные подпакеты
  • Пространства имен пакета
  • Настройка доступа к атрибутам модуля

Пакеты в Python

Пакет в Python – это набор из нескольких файлов модулей в одном каталоге. Это упрощает переносимость всех зависимостей приложения в одной папке. Мы используем точечную нотацию для доступа к функциям модуля внутри пакета.

Например, если вы хотите получить доступ к модулю «sample_module» в пакете с именем «sample_package», вы можете сделать это с помощью sample_package.sample_module.

Короче говоря, пакет Python упрощает работу с несколькими модулями.

Как создать пакет?

Предположим, вы хотите создать набор модулей для обработки музыкальных файлов. Взгляните на следующую структуру. Вот как вы организуете различные файлы в папке вашего пакета. В нашем случае папка пакета верхнего уровня – это «music»:

music/ Top-level package __init__.py Initialize the music package formats/ Subpackage for file conversions __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py . effects/ Subpackage for sound effects __init__.py echo.py surround.py reverse.py . filters/ Subpackage for filters __init__.py equalizer.py vocoder.py karaoke.py .

Каждый пакет в Python должен иметь файл __init__.py, который гарантирует, что этот каталог будет рассматриваться как пакет.

Как правило, __init__.py может быть просто пустым файлом или исполняемым кодом инициализации для пакета или задавать переменную __all__, которая будет рассмотрена в последней части этого руководства.

Импортировать отдельный модуль из пакета можно любым из следующих способов.

import music.formats.wavwrite
from music.formats import wavwrite

Приведенные выше операторы загружают подмодуль music.formats.wavwrite.

Предположим, в модуле wavwrite.py есть функция с именем writeFile (aFileName), которая принимает имя файла в качестве аргумента, мы называем ее, как показано ниже:

import music.formats.wavwrite . . music.formats.wavwrite.writeFile(outputFileName)
from music.formats import wavwrite . . wavwrite.writeFile(outputFileName)

Мы можем пойти еще глубже в операторе import, где мы импортируем только ту функцию, которая нам нужна. Вот пример того, как вы можете сделать то же самое:

from music.formats.wavwrite import writeFile . . writeFile(outputFileName)

Как импортировать все модули из пакета?

А что, если писать из файла music.formats import *, как мы это делали при импорте из модуля, не было бы проще?

Хотя это, безусловно, проще, проблема будет в чрезмерном использовании памяти, поскольку большинство функций не будут использоваться в ваших программах.

Идеальное решение – это когда автор пакета предоставляет явный индекс пакета. Если код __init__.py пакета определяет список с именем __all__, он будет рассматриваться как индекс имен модулей, которые должны быть импортированы при обнаружении from music.formats import *.

Давайте посмотрим на другой пример, чтобы лучше понять эту концепцию. Предположим, у нас есть такая структура пакета: q.

Пример структуры пакета

Здесь вы можете увидеть, что в разделе \music есть файл __init__.py. Если __all__ определено ниже:

__all__ = ["admin", "apps", "models"]

Тогда будут импортированы только субмодули, перечисленные в приведенном выше списке, пока обнаруживается импорт из music*.

Если __all__ не определен, оператор from music import * не будет импортировать все подмодули из пакета. Заявление от импорта music* только гарантирует, что музыкальный пакет был импортирован.

Пакеты в Python

В этой статье вы научитесь разбивать вашу программу на компактные и элегантные модули — пакеты. Также вы узнаете как импортировать собственные и сторонние пакеты в ваши проекты.

Что такое пакеты

Никто из нас не хранит все файлы на компьютере в одном месте. У каждого из нас хорошо организованная иерархия папок — так проще найти нужный файл.

Файлы одного и того же типа хранятся в одной папке. Например, песни мы храним в папке «музыка». То же самое и в Python — для разных фрагментов кода свои директории и модули.

С ростом количества строк кода в проекте растет и количество модулей. Одни модули мы храним в одном пакете, другие — в другом. Так проектом проще управлять, он становится понятнее.

Как папки содержат подпапки, так и пакеты Python содержит подпакеты и модули.

Чтобы Python воспринимал папку как пакет, в ней должен содержаться файл __init__.py . Он может быть пустым, но обычно в нем содержится инициализационный код этого пакета.

Приведем пример — допустим, вы разрабатываете игру. На картинке представлен возможный вариант организации пакетов и модулей.

Импорт модулей из пакета

Мы можем импортировать модули из пакетов с помощью оператора-точки — . .

Допустим, нам нужно импортировать модуль start . Делается это так:

import Game.Level.start

Если в этом модуле есть функция select_difficulty() , то вызвать мы ее можем так:

Game.Level.start.select_difficulty(2)

Конструкцию можно сократить — нужно лишь импортировать модуль без префикса пакета:

from Game.Level import start

Теперь функцию можно вызывать следующим образом:

start.select_difficulty(2)

Еще один способ импортирования нужной функции (класса или переменной) из модуля внутри пакета:

from Game.Level.start import select_difficulty

Теперь функцию можно вызывать напрямую:

select_difficulty(2)

Несмотря на простоту, использовать этот способ не рекомендуется. Причина проста — использование полного пространства имен убережет вас от ошибок. Например, если в проекте встретятся два одинаковых имени идентификаторов.

Во время импорта пакетов Python просматривает список директорий в sys.path — точно так же, как в случае с модулями.

СodeСhick.io — простой и эффективный способ изучения программирования.

2023 © ООО «Алгоритмы и практика»

Python модули и пакеты

При написании объёмного кода, часто прибегают к разбиению такового на логически независимые блоки и к последующему выносу в другие файлы. Это повышает читаемость как самого кода, так и проекта целиком. Что влечет за собой менее ресурсозатратную поддержку(дальнейшую модификацию кодовой базы для разных нужд).

После разделения кода по файлам, следует выстроить их взаимодействие. В языке программирования Python данный механизм реализуется с использованием import. Импортировать можно любые компоненты(если Вы кодом не ограничивали таковые) модулей или пакетов.

Модули также могут быть написаны и на других языках, но данная статья направлена на рассмотрение использования данного механизмах в рамках Python.

Модули

В языке программирования Python модулями являются все файлы с расширением *.py (* обозначает, что на этом месте может стоять любой символ или любое их количество). Исключением является служебный файл __init__.py (о назначении которого описано далее в статье).

Дальше стоит понимать, что любая программа имеет некую точку входа. Это своего рода место с которого стартует наш скрипт. В языках предшественниках данной точкой служила функция main и могла быть лишь только одной. В нашем случае допускается отсутствие таковой, но это снижает качество кода, делая его сложным и малопредсказуемым(при импорте код содержащийся на верхнем уровне исполняется). Для того чтобы указать точку входа(может быть указана только в модулях) используется специальная переменная __name__ , в которой содержится наименование текущего модуля или пакета. Если текущий модуль находится на верхнем уровне исполнения(мы явно его передали на исполнение Python), то он называется __main__ независимо от названия файла.

# Указание входной точки ## Если __name__ равно "__main__" исполни if __name__ == "__main__": print('Я главный!') # Вызов других функций, например main()

Для примера реализуем простой модуль, который будет возвращать нам информацию:

# http_get.modules.http_get # Расположен в дирректории http_get, modules и назван http_get def get_dict(): return # Поскольку зависимых импортов нет, мы можем исполнить этот код для проверки # Т.е. в качестве входной точки использовать нашу функцию # Данный код исполнится только, когда этот файл будет исполняемым(не импортируемым) if __name__ == '__main__': print(get_dict())

Далее в корне создадим main.py файл, в который импортируем наш модуль двумя разными способами(об импортах описано в статье):

# main.py from ModulesAndPackages.module_examples.http_get.modules.http_get import get_dict as absolute from http_get.modules.http_get import get_dict as relative def main(): # Работает print(absolute()) print(relative()) if __name__ == '__main__': main()

Все без проблем исполняется.

Трудности

При переносе файлов куда-либо из директории возникнут проблемы из-за первого импорта( main.py ). Поскольку часто приходится писать один и тот же код, использование уже хорошо написанного пакета или модуля может экономить много времени, но исправление большого количества импортов требует Ваших ресурсов. Хорошо написанный пакет, модуль или импорт может экономить ваши рабочие часы, а иногда и нервы.

Не изменяя наши модули(импорты), при изменении положения файлов возникает ошибка импорта:

# Не работает в другом проекте from ModulesAndPackages.module_examples.http_get.modules.http_get import get_dict as absolute # Всегда работает from http_get.modules.http_get import get_dict as relative def main(): print(absolute()) print(relative()) if __name__ == '__main__': main()

Пакеты

В языке программирования Python пакетами являются все директории(вне зависимости от наличия в них модулей), содержащие файл __init__.py , который исполняется при импорте пакета и несет его название ( __name__ ).

Для примера реализуем простой пакет( package ), на базе вышеописанного модуля( http_get.py ):

# package/modules/http_get.py def get_dict(): return if __name__ == '__main__': print(get_dict())
# package/__init__.py from .modules.http_get import get_dict . def get_data(): return get_dict() . # Не работает # __init__ не может иметь точки входа # # if __name__ == '__main__': # get_data()

А также реализуем простой пакет с такой же логикой, но с использованием абсолютного импорта:

# package_2/modules/http_get.py def get_dict(): return if __name__ == '__main__': print(get_dict()) 
# package_2/__init__.py from ModulesAndPackages.package_examples.package_2.modules.http_get import get_dict . def get_data(): return get_dict() . # Не работает # __init__ не может иметь точки входа # # if __name__ == '__main__': # get_data()

В корне директории(на уровень выше пакета) создадим файл, в котором воспользуемся нашими пакетами( main.py ):

# main.py from package import get_data from package_2 import get_data as get_data_2 def main(): # Работает print(get_data()) print(get_data_2()) if __name__ == '__main__': main()

Все работает без ошибок.

Трудности

Но при переносе нашего package_2 в другой проект, он теряет свою работоспособность из-за ошибки импортирования в __init__.py файле, в отличии от package .

# package_transferring/package/modules/http_get.py def get_dict(): return if __name__ == '__main__': print(get_dict())
# package_transferring/package/__init__.py from .modules.http_get import get_dict . def get_data(): return get_dict() . # Не работает # __init__ не может иметь точки входа # # if __name__ == '__main__': # get_data()
# package_transferring/package_2/modules/http_get.py def get_dict(): return if __name__ == '__main__': print(get_dict())
# package_transferring/package_2/__init__.py # Ошибка импорта т.к. изменилась директория from ModulesAndPackages.package_examples.package_2.modules.http_get import get_dict . def get_data(): return get_dict() . # Does not work! # Because init file in package could not have entry point # # if __name__ == '__main__': # get_data()
# package_transferring/main.py from package import get_data # Ошибка импорта from package_2 import get_data as get_data_2 def main(): print(get_data()) print(get_data_2()) if __name__ == '__main__': main()

P.S.

Данная статья написана для новичков, которые изучают язык программирования Python. Задача которой продемонстрировать на простых примерах способы написания пакетов и модулей(не является туториалом), а так же показать какие трудности могут возникнуть и пути их решения.

Github с проектом к данной статье: ModulesAndPackages

Может быть полезно выгрузить модуль или пакет и попробовать внедрить его в свой проект.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *