Пакеты модулей
Модуль - 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)