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

Модуль - 1 файл.

Пакет - 1 директория.

Можно импортировать пакеты.

Где указывали имя файла, можно указать список директорий через точку.

import dir1.dir2.mod

или

from dir1.dir2.mod import x

Т.е предполагается, что есть директория dir0 (находится в пути поиска). В нем есть директория dir1, в которой есть dir2, в которой лежит модуль mod (в котором есть атрибут х).

Пишем через точку, получаем платформо-независимый доступ к директориям пакета.

Делаем пакет. Файл __init__.py

Каждая директория, которая указана в пути импорта, должна содержать файл __init__.py

То есть в директориях dir1 и dir2 такие файлы должны быть. В директории dir0 - не обязательно (она не указана в инструкции import).

dir0\           # <- должен быть в sys.path
    dir1\
        __init__.py
        dir2\
            __init__.py
            mod.py

Что должно лежать в файле __init__.py? Можно оставить его пустым. Можно указать, что будет импортироваться при from модуль import *.

  • Инициализация пакета - при первом импорте каталога, питон выполняет код в файле __init__.py этого каталога. Здесь можно открыть соединение с БД, создать файл с данными и тп.

  • Инициализация пространства имен модуля. Получаем дерево вложенных объектов. Далее можно использовать dir1.dir2 - этот объект содержит все имена, которые определяет __init__.py из dir2.

В dir1 может не быть модулей (других файлов, кроме __init__.py).

__init__.py - создает пространство имен для объектов модулей (особенно, если модулей в смысле файлов, в директории нет).

  • from .. * - импортируется или все имена из каталога, либо, если в __init__.py определен список __all__, то только имена из этого списка.

Пример импортирования пакета

Определим __init__.py и mod.py файлы описанного дерева:

# Файл: dir1\__init__.py
Print('dir1 init')
x = 1
# Файл: dir1\dir2\__init__.py
Print('dir2 init')
y = 2
# Файл: dir1\dir2\mod.py
Print('in mod.py')
z = 3

Запускаем:

% python
>>> import dir1.dir2.mod    # Сначала запускаются файлы инициализации
dir1 init
dir2 init
in mod.py
>>>
>>> import dir1.dir2.mod    # Повторное импортирование не выполняется
>>>
>>> from imp import reload  # Требуется в версии 3.0
>>> reload(dir1)
dir1 init
<module 'dir1' from 'dir1\__init__.pyc'>
>>>
>>> reload(dir1.dir2)
dir2 init
<module 'dir1.dir2' from 'dir1\dir2\__init__.pyc'>

Вложенные объекты:

>>> dir1
<module 'dir1' from 'dir1\__init__.pyc'>
>>> dir1.dir2
<module 'dir1.dir2' from 'dir1\dir2\__init__.pyc'>
>>> dir1.dir2.mod
<module 'dir1.dir2.mod' from 'dir1\dir2\mod.pyc'>

Можно доступиться к переменным, определенным в __init__.py и mod.py файлах:

>>> dir1.x
1
>>> dir1.dir2.y
2
>>> dir1.dir2.mod.z
3

Как написать короче

Приходится писать полные пути, короткие не работают:

>>> dir2.mod
NameError: name ‘dir2’ is not defined
>>> mod.z
NameError: name ‘mod’ is not defined

Лучше всего использовать as

% python
>>> from dir1.dir2 import mod   # Описание пути находится только в этом месте
dir1 init
dir2 init
in mod.py
>>> mod.z                       # Указывать полный путь не требуется
3
>>> from dir1.dir2.mod import z
>>> z
3
>>> import dir1.dir2.mod as mod # Использование короткого синонима
>>> mod.z                       # теперь работает
3

Запуск модуля

Все указанные директории обязаны иметь __ini__.py файлы.

Стоим в dir0:

python3 -m dir1.dir2.mod

Запуск пакета

Написанные на питоне сервисы обычно оформляют как пакеты. Для запуска используется корневой модуль пакета, т.е. запуск пакета как программы.

В корневой директории пакета должен быть файл __init__.py, в нем, с указанием if __name__ == '__main__' должен быть разбор аргументов командной строки при запуске и запуск сервиса.

Когда нужно импортировать пакеты

Разумно в путь поиска добавлять только корневую директорию проекта и дальше использовать импортирование пакетов (а не перечислять там все директории).

Разрешение неоднозначностей

Пусть есть проект1 в директории system1:

system1\
    utilities.py    # Общие вспомогательные функции, классы
    main.py         # Этот файл запускает программу
    other.py        # Импортирует и использует модуль utilities

И другой проект2 в директории system2:

system2\
    utilities.py    # Общие вспомогательные функции, классы
    main.py         # Этот файл запускает программу
    other.py        # Импортирует и использует модуль utilities

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

Теперь я хочу в новом проекте использовать как функции из system1\utilities.py, так и из system2\utilities.py

Допустим, оба проекта у нас в пути поиска модулей. Тогда можно написать:

import utilities
utilities.func('spam')

Но из какого пакета нам достался модуль utilities?

Можно поставить очередность каталогов в sys.path. Какой из них сделать первым?

Разумно объединить проекты в одной директории dir0:

dir0\
    __init__.py      # работает и без этого, но хорошо бы добавить и корневой инит-файл
    system1\
        __init__.py     # добавили
        utilities.py
        main.py         # импорты здесь работают по-старому
        other.py
    system2\
        __init__.py     # добавили
        utilities.py
        main.py         # импорты здесь работают по-старому
        other.py        
    system3\            # Здесь или в другом месте
        __init__.py     # располагается ваш новый программный код
        myfile.py

теперь можно импортировать в myfile с указанием каталогов:

import system1.utilities
import system2.utilities
system1.utilities.function('spam')
system2.utilities.function('eggs')

а еще лучше - написать разные as

import system1.utilities as util1   # придумайте себе более разумные сокращения, по типу утилит
import system2.utilities as util2
util1.function('spam')
util2.function('eggs')

Не обязательно system3 класть в ту же dir0, но раз проекты берут что-то друг у друга, вдруг будет проект system4, который будет брать разные части из предыдущих трех проектов. Сразу задумаемся о будущем удобстве.

Изменения в Python 3.0

Лутц стр 650 Принцип действия операции импортирования внутри пакетов немного изме- нился в Python 3.0. Изменения коснулись лишь импортирования файлов паке- та из файлов, находящихся в каталогах этого же пакета, о котором мы говорим в этой главе, – операция импортирования других файлов действует, как и пре- жде. В Python 3.0 в операцию импортирования внутри пакетов было внесено два изменения:

  • Изменилась семантика пути поиска модулей так, что теперь операция им- портирования модуля по умолчанию пропускает собственный каталог па- кета. Она проверяет только компоненты пути поиска. Эта операция называ- ется импортированием по «абсолютному» пути.
  • Расширен синтаксис инструкции from так, что теперь имеется возможность явно указать, что поиск импортируемых модулей должен производиться только в каталоге пакета. Эта операция называется импортированием по «относительному» пути.

Суть этих изменений в версии 3.0 (и в 2.6, если они используются) состоит в том, что вы должны использовать специальный синтаксис инструкции from для импортирования модулей, находящихся в том же пакете, что и импорти- рующий модуль, если вы не указываете полный путь к модулю, начиная от корневого каталога пакета. Если не использовать этот синтаксис, интерпрета- тор не сможет отыскать требуемый модуль в пакете.

Относительные пути

Это пути с . или ..

  • Если в пути указана . или .., то поиск модуля начинается с текущей директории (или директории выше)
  • Если в пути не указаны точки в начале, то текущая директория не входит в путь поиска модулей.

Относительный путь можно указать только в операции from, в import - нельзя

Запускайте программу как модуль, иначе у нее только __main__ модуль и имеется: Note that relative imports are based on the name of the current module. Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application must always use absolute imports.

import string   # Пропустит поиск модуля в пакете, взять build-in модуль string
from . import string    # Поиск выполняется только в пределах пакета

Пусть у нас есть пакет mypkg с модулем string:

from . import string # Импортирует mypkg.string (относительно пакета)
from .string import name1, name2 # Импортирует имена из mypkg.string

Пусть мы вызваем в модуле А.В.С импорты:

from . import D     # Импортирует A.B.D (. означает A.B)
from .. import E    # Импортирует A.E (.. означает A)
from .D import X    # Импортирует A.B.D.X (. означает A.B)
from ..E import X   # Импортирует A.E.X (.. означает A)

Импортирование по относительному пути происходит:

  • только внутри пакета;
  • только инструкция from;

Правила поиска модулей:

  • Для простых имен пакетов (например, A) поиск выполняется во всех директориях sys.path, слева направо. Этот список делается из системных значений по умолчанию и из настроек пользователя.
  • пакет - это директория с модулями (и файлом __ini__.py в каждой поддиректории). Чтобы доступиться к A.B.C в sys.path должна быть директория А.
  • модули по абсолютным путям ищутся как раньше
  • модули по относительным путям идет относительно текущего пакета (а обычный поиск в sys.path не выполняется) from . import A - ищет только в текущей директории пакета.

Примеры

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

Если в текущей директории нет файла string.py, то этот код загружает стандартный модуль string (т.е. импорт внешних модулей работает как обычно).

C:\test> c:\Python30\python
>>> import string
>>> string
<module 'string' from 'c:\Python30\lib\string.py'>

Добавим в текущий каталог string.py:

# test\string.py
print('I am local string module')
C:\test> c:\Python30\python
>>> import string
I am local string module
>>> string
<module 'string' from 'string.py'>

Вывод: обычный импорт сначала ищет в текущем каталоге.

Пакета на этом уровне нет (так как нет __ini__.py), поэтому поиск по . не проходит:

>>> from . import string
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Attempted relative import in non-package

Импорт в других файлах тоже работает:

# test\main.py
import string
print(string)
C:\test> C:\python30\python main.py # Тот же результат получается в 2.6
I am local string module
<module 'string' from 'C:\test\string.py'>

Импортирование внутри пакета

Удалим локальный string.py и создадим директорию pkg с пустым __init__.py:

C:\test> del string*
C:\test> mkdir pkg
# test\pkg\spam.py
import eggs # <== Работает в 2.6, но не в 3.0!
print(eggs.X)
# test\pkg\eggs.py
X = 99999
import string
print(string)

results matching ""

    No results matching ""