1 added
1 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Статья рассказывает об устройстве пакетов и модулей языка Python и раскрывает некоторые тонкости, о которых следует знать при работе с пакетами, моделями и их импортом.</p>
1
<p>Статья рассказывает об устройстве пакетов и модулей языка Python и раскрывает некоторые тонкости, о которых следует знать при работе с пакетами, моделями и их импортом.</p>
2
<h2>Содержание</h2>
2
<h2>Содержание</h2>
3
<ul><li><a>О чём пойдёт речь</a></li>
3
<ul><li><a>О чём пойдёт речь</a></li>
4
<li><a>Кратко о модулях</a></li>
4
<li><a>Кратко о модулях</a></li>
5
<li><a>Модули и видимость содержимого</a></li>
5
<li><a>Модули и видимость содержимого</a></li>
6
<li><a>Наконец-то, пакеты!</a></li>
6
<li><a>Наконец-то, пакеты!</a></li>
7
<li><a>Пакеты, модули и точки входа</a></li>
7
<li><a>Пакеты, модули и точки входа</a></li>
8
<li><a>PEP 420, или неявные пространства имён</a></li>
8
<li><a>PEP 420, или неявные пространства имён</a></li>
9
<li><a>Циклические импорты</a></li>
9
<li><a>Циклические импорты</a></li>
10
<li><a>Поиск пакетов и модулей</a></li>
10
<li><a>Поиск пакетов и модулей</a></li>
11
<li><a>Что не было раскрыто?</a></li>
11
<li><a>Что не было раскрыто?</a></li>
12
</ul><h2>О чём пойдёт речь</h2>
12
</ul><h2>О чём пойдёт речь</h2>
13
<p>Как вы, возможно знаете, код на Python хранится в<em>модулях (modules)</em>, которые могут быть объединены в<em>пакеты (packages)</em>. Это руководство призвано подробно рассказать именно о пакетах, однако совсем не упомянуть модули нельзя, поэтому я немного расскажу и о них. Многое из того, что применимо к модулям, справедливо и для пакетов, особенно если принять во внимание тот факт, что каждый, как правило, ведёт себя как модуль.</p>
13
<p>Как вы, возможно знаете, код на Python хранится в<em>модулях (modules)</em>, которые могут быть объединены в<em>пакеты (packages)</em>. Это руководство призвано подробно рассказать именно о пакетах, однако совсем не упомянуть модули нельзя, поэтому я немного расскажу и о них. Многое из того, что применимо к модулям, справедливо и для пакетов, особенно если принять во внимание тот факт, что каждый, как правило, ведёт себя как модуль.</p>
14
<h2>Кратко о модулях</h2>
14
<h2>Кратко о модулях</h2>
15
<p>Модуль в Python - это файл с кодом. Во время же исполнения модуль представлен соответствующим объектом, атрибутами которого являются:</p>
15
<p>Модуль в Python - это файл с кодом. Во время же исполнения модуль представлен соответствующим объектом, атрибутами которого являются:</p>
16
<ol><li>Объявления, присутствующие в файле.</li>
16
<ol><li>Объявления, присутствующие в файле.</li>
17
<li>Объекты, импортированные в этот модуль откуда-либо.</li>
17
<li>Объекты, импортированные в этот модуль откуда-либо.</li>
18
</ol><p>При этом определения и импортированные сущности ничем друг от друга не отличаются: и то, и другое - это всего лишь именованные ссылки на некоторые объекты первого класса (такие, которые могут быть переданы из одного участка кода в другой как обычные значения).</p>
18
</ol><p>При этом определения и импортированные сущности ничем друг от друга не отличаются: и то, и другое - это всего лишь именованные ссылки на некоторые объекты первого класса (такие, которые могут быть переданы из одного участка кода в другой как обычные значения).</p>
19
<p>Такое единообразие удобно, например, при рефакторинге: мы можем разделить один разросшийся модуль на несколько, а потом импортировать вынесенные определения в оригинальный модуль. При этом с точки зрения внешнего наблюдателя переработанный модуль будет иметь те же атрибуты, которые имел до внесения изменений, а значит у пользователей модуля ничего в коде не сломается.</p>
19
<p>Такое единообразие удобно, например, при рефакторинге: мы можем разделить один разросшийся модуль на несколько, а потом импортировать вынесенные определения в оригинальный модуль. При этом с точки зрения внешнего наблюдателя переработанный модуль будет иметь те же атрибуты, которые имел до внесения изменений, а значит у пользователей модуля ничего в коде не сломается.</p>
20
<h2>Модули и видимость содержимого</h2>
20
<h2>Модули и видимость содержимого</h2>
21
<p>В Python нет настоящего сокрытия атрибутов объектов, поэтому и атрибуты объекта модуля так или иначе всегда доступны после импорта последнего. Однако существует ряд соглашений, которые влияют на процесс импортирования и поведение инструментов, работающих с кодом.</p>
21
<p>В Python нет настоящего сокрытия атрибутов объектов, поэтому и атрибуты объекта модуля так или иначе всегда доступны после импорта последнего. Однако существует ряд соглашений, которые влияют на процесс импортирования и поведение инструментов, работающих с кодом.</p>
22
<p>Так атрибуты, имя которых начинается с одиночного подчёркивания, считаются как бы помеченными "для внутреннего использования", и обычно не отображаются в IDE при обращению к объекту "через точку". И linter обычно предупреждает об использовании таких атрибутов, мол, "небезопасно!". "Опасность" состоит в том, что автор кода имеет полное право<strong>изменять состав таких атрибутов без уведомления пользователей</strong>кода. Поэтому программист, использовавший в своём коде приватные части чужого кода рискует в какой-то момент получить код, который перестанет работать при обновлении сторонней библиотеки.</p>
22
<p>Так атрибуты, имя которых начинается с одиночного подчёркивания, считаются как бы помеченными "для внутреннего использования", и обычно не отображаются в IDE при обращению к объекту "через точку". И linter обычно предупреждает об использовании таких атрибутов, мол, "небезопасно!". "Опасность" состоит в том, что автор кода имеет полное право<strong>изменять состав таких атрибутов без уведомления пользователей</strong>кода. Поэтому программист, использовавший в своём коде приватные части чужого кода рискует в какой-то момент получить код, который перестанет работать при обновлении сторонней библиотеки.</p>
23
<p>Итак, мы можем определять<em>публичные атрибуты</em>модуля,<em>приватные атрибуты</em>(так называют упомянутые выше атрибуты "для внутреннего пользования"). И данное разделение касается не только определений, содержащихся в самом модуле, но и импортируемых сущностей. Ведь все<strong>импортированные объекты становятся атрибутами и того модуля, в который они импортированы</strong>.</p>
23
<p>Итак, мы можем определять<em>публичные атрибуты</em>модуля,<em>приватные атрибуты</em>(так называют упомянутые выше атрибуты "для внутреннего пользования"). И данное разделение касается не только определений, содержащихся в самом модуле, но и импортируемых сущностей. Ведь все<strong>импортированные объекты становятся атрибутами и того модуля, в который они импортированы</strong>.</p>
24
<p>Есть и третья группа атрибутов - атрибуты, добавляемые в область видимости при импортировании всего содержимого модуля ("со звёздочкой", from module import *). Если ничего явно не указывать, то при таком импортировании в текущую область видимости добавятся<strong>все публичные атрибуты модуля</strong>. Помимо данного умолчания существует и возможность явно указать, что конкретно будет экспортировано при импорте со звёздочкой. Для управления названным методом импорта существует атрибут __all__, в который можно положить список (а ещё лучше - кортеж)<strong>строк с именами, которые будут экспортироваться</strong>.</p>
24
<p>Есть и третья группа атрибутов - атрибуты, добавляемые в область видимости при импортировании всего содержимого модуля ("со звёздочкой", from module import *). Если ничего явно не указывать, то при таком импортировании в текущую область видимости добавятся<strong>все публичные атрибуты модуля</strong>. Помимо данного умолчания существует и возможность явно указать, что конкретно будет экспортировано при импорте со звёздочкой. Для управления названным методом импорта существует атрибут __all__, в который можно положить список (а ещё лучше - кортеж)<strong>строк с именами, которые будут экспортироваться</strong>.</p>
25
<h3>Живой пример видимости атрибутов модулей.</h3>
25
<h3>Живой пример видимости атрибутов модулей.</h3>
26
<p>Рассмотрим пример, демонстрирующий всё вышеописанное. Пусть у нас будет два файла:</p>
26
<p>Рассмотрим пример, демонстрирующий всё вышеописанное. Пусть у нас будет два файла:</p>
27
<p>Рассмотрим сначала обычный импорт import module. Если импортировать модуль таким образом, то IDE, REPL и остальные инструменты "увидят" у модуля следующие атрибуты:</p>
27
<p>Рассмотрим сначала обычный импорт import module. Если импортировать модуль таким образом, то IDE, REPL и остальные инструменты "увидят" у модуля следующие атрибуты:</p>
28
<ul><li>FISH, MEAT т.к. имена констант - публичные,</li>
28
<ul><li>FISH, MEAT т.к. имена констант - публичные,</li>
29
<li>CAT, т.к. константа импортирована под публичным именем.</li>
29
<li>CAT, т.к. константа импортирована под публичным именем.</li>
30
</ul><p>А эти атрибуты<strong>не</strong>будут видны:</p>
30
</ul><p>А эти атрибуты<strong>не</strong>будут видны:</p>
31
<ul><li>_DOG, т.к. при импортировании константа переименована в приватной манере,</li>
31
<ul><li>_DOG, т.к. при импортировании константа переименована в приватной манере,</li>
32
<li>_GOAT, т.к. импортирована по своему приватному имени (тут линтер может и поругать за обращение к приватному атрибуту модуля!),</li>
32
<li>_GOAT, т.к. импортирована по своему приватному имени (тут линтер может и поругать за обращение к приватному атрибуту модуля!),</li>
33
<li>_CARROT, ибо приватная константа.</li>
33
<li>_CARROT, ибо приватная константа.</li>
34
</ul><blockquote><p>Импорт import other_module я не рассматриваю как тривиальный случай.</p>
34
</ul><blockquote><p>Импорт import other_module я не рассматриваю как тривиальный случай.</p>
35
</blockquote><p>Теперь рассмотрим импорт<strong>всего содержимого module</strong>:</p>
35
</blockquote><p>Теперь рассмотрим импорт<strong>всего содержимого module</strong>:</p>
36
<p>После импортирования<strong>в текущей области видимости</strong>мы получим<strong>одно новое имя</strong>: _CARROT - именно оно перечислено в атрибуте __all__. Заметьте, что в данном случае при массовом импорте добавится даже приватный атрибут, потому что он явно указан!</p>
36
<p>После импортирования<strong>в текущей области видимости</strong>мы получим<strong>одно новое имя</strong>: _CARROT - именно оно перечислено в атрибуте __all__. Заметьте, что в данном случае при массовом импорте добавится даже приватный атрибут, потому что он явно указан!</p>
37
<blockquote><p>Последствия импорта from other_module import * тоже очевидны и я их не рассматриваю.</p>
37
<blockquote><p>Последствия импорта from other_module import * тоже очевидны и я их не рассматриваю.</p>
38
</blockquote><h2>Наконец-то, пакеты!</h2>
38
</blockquote><h2>Наконец-то, пакеты!</h2>
39
<p>Пакет в Python - директория с обязательным модулем __init__.py. Остальное содержимое опционально и может включать в себя и модули, и другие пакеты.</p>
39
<p>Пакет в Python - директория с обязательным модулем __init__.py. Остальное содержимое опционально и может включать в себя и модули, и другие пакеты.</p>
40
<h3>Импортирование пакетов</h3>
40
<h3>Импортирование пакетов</h3>
41
<p>Пакет с единственным модулем __init__.py при импорте ведёт себя как обычный модуль. Содержимое<em>инициализирующего модуля</em>определяет атрибуты объекта пакета.</p>
41
<p>Пакет с единственным модулем __init__.py при импорте ведёт себя как обычный модуль. Содержимое<em>инициализирующего модуля</em>определяет атрибуты объекта пакета.</p>
42
<p>Прочие модули пакета и вложенные пакеты не импортируются автоматически вместе с пакетом-родителем, но могут быть импортированы отдельно с указанием<strong>полного имени</strong>. Важный момент: при импортировании вложенного модуля всегда сначала<strong>импортируются модули инициализации всех родительских пакетов</strong>(если они ещё ни разу не импортировались, но об этом я расскажу ниже).</p>
42
<p>Прочие модули пакета и вложенные пакеты не импортируются автоматически вместе с пакетом-родителем, но могут быть импортированы отдельно с указанием<strong>полного имени</strong>. Важный момент: при импортировании вложенного модуля всегда сначала<strong>импортируются модули инициализации всех родительских пакетов</strong>(если они ещё ни разу не импортировались, но об этом я расскажу ниже).</p>
43
<p>Рассмотрим, к примеру, следующую структуру директорий и файлов:</p>
43
<p>Рассмотрим, к примеру, следующую структуру директорий и файлов:</p>
44
<p>Когда мы импортируем модуль submodule.py, то фактически происходит следующее (именно в таком порядке):</p>
44
<p>Когда мы импортируем модуль submodule.py, то фактически происходит следующее (именно в таком порядке):</p>
45
<ol><li>загружается и выполняется модуль package/__init__.py,</li>
45
<ol><li>загружается и выполняется модуль package/__init__.py,</li>
46
<li>загружается и выполняется package/subpackage/__init__.py,</li>
46
<li>загружается и выполняется package/subpackage/__init__.py,</li>
47
<li>наконец, импортируется package/subpackage/submodule.py.</li>
47
<li>наконец, импортируется package/subpackage/submodule.py.</li>
48
</ol><blockquote><p>При импорте package.module предварительно загружается только package/__init__.py.</p>
48
</ol><blockquote><p>При импорте package.module предварительно загружается только package/__init__.py.</p>
49
</blockquote><p>Так что же, если мы загрузим парочку вложенных модулей, то для каждого будет выполняться загрузка всех __init__.py по дороге? Не будет! Подсистема интерпретатора, отвечающая за загрузку модулей, кэширует уже загруженные пакеты и модули.<strong>Каждый конкретный модуль загружается ровно один раз</strong>, в том числе и инициализирующие модули __init__.py (короткие имена модулей хоть и одинаковы, но полные имена всегда разные). Все последующие импортирования модуля не приводят к его загрузке, только лишь нужные атрибуты копируются в соответствующие области видимости.</p>
49
</blockquote><p>Так что же, если мы загрузим парочку вложенных модулей, то для каждого будет выполняться загрузка всех __init__.py по дороге? Не будет! Подсистема интерпретатора, отвечающая за загрузку модулей, кэширует уже загруженные пакеты и модули.<strong>Каждый конкретный модуль загружается ровно один раз</strong>, в том числе и инициализирующие модули __init__.py (короткие имена модулей хоть и одинаковы, но полные имена всегда разные). Все последующие импортирования модуля не приводят к его загрузке, только лишь нужные атрибуты копируются в соответствующие области видимости.</p>
50
<h3>Пакеты и __all__</h3>
50
<h3>Пакеты и __all__</h3>
51
-
<p>В целом атрибут __all__ в модуле инициализации пакета ведёт себя так же, как и в случае с обычным модулем. Н�� если при импорте пакета "со звёздочкой" среди перечисленных имён встретится имя вложенного модуля, а сам модуль не окажется импортирован ранее в этом же __init__.py, то этот<strong>модуль импортируется неявно</strong>! Очередной пример это продемонстрирует.</p>
51
+
<p>В целом атрибут __all__ в модуле инициализации пакета ведёт себя так же, как и в случае с обычным модулем. Но если при импорте пакета "со звёздочкой" среди перечисленных имён встретится имя вложенного модуля, а сам модуль не окажется импортирован ранее в этом же __init__.py, то этот<strong>модуль импортируется неявно</strong>! Очередной пример это продемонстрирует.</p>
52
<p>Вот структура пакета:</p>
52
<p>Вот структура пакета:</p>
53
<p>Файл же package/__init__.py содержит следующее (и только это!):</p>
53
<p>Файл же package/__init__.py содержит следующее (и только это!):</p>
54
<p>А импортируем мы from package import *. В области видимости у нас окажутся объекты модулей a и b под своими именами (без полного пути, то есть без package.). При этом сами модули в коде нигде явно не импортируются! Такая вот "автомагия".</p>
54
<p>А импортируем мы from package import *. В области видимости у нас окажутся объекты модулей a и b под своими именами (без полного пути, то есть без package.). При этом сами модули в коде нигде явно не импортируются! Такая вот "автомагия".</p>
55
<p>Указанный автоматизм достаточно ограничен: не работает "вглубь", например - не импортирует "через звёздочку" указанные модули и подпакеты. Если же вам вдруг такого захочется, вы всегда сможете на соответствующих уровнях в __init__.py сделать from x import * и получить в корневом пакете плоскую область видимости со всем нужным содержимым. Но такое нужно довольно редко, потому что "не помогает" ни IDE, ни ручному поиску по коду. Впрочем, знать о фиче и иметь её в виду - не вредно, как мне кажется.</p>
55
<p>Указанный автоматизм достаточно ограничен: не работает "вглубь", например - не импортирует "через звёздочку" указанные модули и подпакеты. Если же вам вдруг такого захочется, вы всегда сможете на соответствующих уровнях в __init__.py сделать from x import * и получить в корневом пакете плоскую область видимости со всем нужным содержимым. Но такое нужно довольно редко, потому что "не помогает" ни IDE, ни ручному поиску по коду. Впрочем, знать о фиче и иметь её в виду - не вредно, как мне кажется.</p>
56
<h2>Пакеты, модули и точки входа</h2>
56
<h2>Пакеты, модули и точки входа</h2>
57
<p>С модулем __init__.py разобрались. Настала очередь модуля __main__.py. Этот модуль позволяет сделать пакет исполняемым посредством вызова python -m …. Те из вас, кому знакомо оформление точки входа в модулях, могут догадаться, откуда ноги растут у магического выражения __name__ == '__main__' - да, отсюда! Для остальных напоминаю: чтобы модуль сделать "исполняемым, но не при импорте", в конец модуля дописывается конструкция</p>
57
<p>С модулем __init__.py разобрались. Настала очередь модуля __main__.py. Этот модуль позволяет сделать пакет исполняемым посредством вызова python -m …. Те из вас, кому знакомо оформление точки входа в модулях, могут догадаться, откуда ноги растут у магического выражения __name__ == '__main__' - да, отсюда! Для остальных напоминаю: чтобы модуль сделать "исполняемым, но не при импорте", в конец модуля дописывается конструкция</p>
58
<p>У модуля, который скармливается интерпретатору напрямую (python file.py) или в роли претендента на запуск (python -m module), атрибут __name__ будет содержать то самое магическое '__main__'. А в остальное время атрибут содержит полное имя модуля. С помощью условия, показанного выше, модуль может решить, что делать при запуске.</p>
58
<p>У модуля, который скармливается интерпретатору напрямую (python file.py) или в роли претендента на запуск (python -m module), атрибут __name__ будет содержать то самое магическое '__main__'. А в остальное время атрибут содержит полное имя модуля. С помощью условия, показанного выше, модуль может решить, что делать при запуске.</p>
59
<p>У пакетов роль атрибута выполняет специальный файл __main__.py. Когда мы запустим пакет через python path/to/package или python -m package, интерпретатор будет искать и выполнять именно этот файл.</p>
59
<p>У пакетов роль атрибута выполняет специальный файл __main__.py. Когда мы запустим пакет через python path/to/package или python -m package, интерпретатор будет искать и выполнять именно этот файл.</p>
60
<p>Более того, модули __main__ нельзя импортировать обычным способом, поэтому можно не бояться случайного импорта и писать команды прямо на верхнем уровне: всё равно странно в модуле с именем __main__ проверять, что его имя равно __main__ (хе-хе!).</p>
60
<p>Более того, модули __main__ нельзя импортировать обычным способом, поэтому можно не бояться случайного импорта и писать команды прямо на верхнем уровне: всё равно странно в модуле с именем __main__ проверять, что его имя равно __main__ (хе-хе!).</p>
61
<p>А ещё модуль __main__.py удобен тем, что его можно класть в корень вашего проекта, после чего запускать проект можно будет с помощью команды python .! Лаконично, не правда ли?</p>
61
<p>А ещё модуль __main__.py удобен тем, что его можно класть в корень вашего проекта, после чего запускать проект можно будет с помощью команды python .! Лаконично, не правда ли?</p>
62
<h2>PEP 420, или неявные пространства имён</h2>
62
<h2>PEP 420, или неявные пространства имён</h2>
63
<p>Раз уж развёл ликбез, расскажу и про эту штуку.</p>
63
<p>Раз уж развёл ликбез, расскажу и про эту штуку.</p>
64
<p>Долгое время в Python пакеты были обязаны иметь файл __init__.py - наличие этого файла позволяло отличить пакет от обычной директории с модулями (с которыми Python работать не мог). Но с версии Python3.3 вступил в силу<a>PEP 420</a>, позволяющий создавать пространства имён "на вырост".</p>
64
<p>Долгое время в Python пакеты были обязаны иметь файл __init__.py - наличие этого файла позволяло отличить пакет от обычной директории с модулями (с которыми Python работать не мог). Но с версии Python3.3 вступил в силу<a>PEP 420</a>, позволяющий создавать пространства имён "на вырост".</p>
65
<p>Теперь вы можете создавать пакет без __init__.py, и такой пакет сможет существовать полноценно, разве что при импорте содержимого не будет производиться инициализация. Но, конечно же, данное изменение делалось не с целью сэкономить на файлах. Подобные пакеты могут встречаться в путях поиска пакетов (о поиске пакетов я ниже расскажу) более одного раза: все встреченные структуры с общим корневым именем при загрузке схлопнутся в одно пространство имён.</p>
65
<p>Теперь вы можете создавать пакет без __init__.py, и такой пакет сможет существовать полноценно, разве что при импорте содержимого не будет производиться инициализация. Но, конечно же, данное изменение делалось не с целью сэкономить на файлах. Подобные пакеты могут встречаться в путях поиска пакетов (о поиске пакетов я ниже расскажу) более одного раза: все встреченные структуры с общим корневым именем при загрузке схлопнутся в одно пространство имён.</p>
66
<p>Тут стоит отметить, что с полноценными пакетами подобное не срабатывало ранее и не будет работать в будущем. Если среди путей пакет с модулем инициализации находится в первый раз, все последующие пакеты с тем же именем будут проигнорированы. Это защищает вас от смешивания сторонних пакетов с системными. И даже просто от ошибок именования: назвав пакет так же, как называется встроенный пакет или модуль, вы получите ошибку - ваши определения не будут импортироваться.</p>
66
<p>Тут стоит отметить, что с полноценными пакетами подобное не срабатывало ранее и не будет работать в будущем. Если среди путей пакет с модулем инициализации находится в первый раз, все последующие пакеты с тем же именем будут проигнорированы. Это защищает вас от смешивания сторонних пакетов с системными. И даже просто от ошибок именования: назвав пакет так же, как называется встроенный пакет или модуль, вы получите ошибку - ваши определения не будут импортироваться.</p>
67
<p><em>Пакеты - пространства имён (Namespace Packages, NP)</em>- а именно так называются пакеты без инициализации - не могут объединяться с полноценными пакетами, поэтому добавить что-то в системный пакет вам также не удастся. И тут всё защищено!</p>
67
<p><em>Пакеты - пространства имён (Namespace Packages, NP)</em>- а именно так называются пакеты без инициализации - не могут объединяться с полноценными пакетами, поэтому добавить что-то в системный пакет вам также не удастся. И тут всё защищено!</p>
68
<p>Какая же польза от неявных пространств имён? А вы представьте себя авторами, скажем, игрового движка. Вы хотите весь код держать в общем пространстве имён engine, но при этом не желаете, чтобы весь код поставлялся одним дистрибутивом (не каждому же пользователю нужны все-все компоненты движка). С NP вы можете в нескольких дистрибутивах использовать<strong>общее корневое имя</strong>engine, но разные подпакеты и подмодули. А на выходе вы получите возможность делать импорты вида</p>
68
<p>Какая же польза от неявных пространств имён? А вы представьте себя авторами, скажем, игрового движка. Вы хотите весь код держать в общем пространстве имён engine, но при этом не желаете, чтобы весь код поставлялся одним дистрибутивом (не каждому же пользователю нужны все-все компоненты движка). С NP вы можете в нескольких дистрибутивах использовать<strong>общее корневое имя</strong>engine, но разные подпакеты и подмодули. А на выходе вы получите возможность делать импорты вида</p>
69
<p><strong>Важно</strong>: помните, если встретятся обычный пакет и NP с одинаковым именем, то<strong>победит</strong>обычный пакет! А NP, сколько бы их не было, не будут загружены!</p>
69
<p><strong>Важно</strong>: помните, если встретятся обычный пакет и NP с одинаковым именем, то<strong>победит</strong>обычный пакет! А NP, сколько бы их не было, не будут загружены!</p>
70
<h2>Циклические импорты</h2>
70
<h2>Циклические импорты</h2>
71
<p>Если вдруг вы захотите в один модуль импортировать другой, а другой захочет, пусть даже и не напрямую, импортировать первый, то вы получите ImportError. Потому что у вас случится циклический импорт. Про него нужно просто знать и стараться архитектурить код так, чтобы циклов не случалось.</p>
71
<p>Если вдруг вы захотите в один модуль импортировать другой, а другой захочет, пусть даже и не напрямую, импортировать первый, то вы получите ImportError. Потому что у вас случится циклический импорт. Про него нужно просто знать и стараться архитектурить код так, чтобы циклов не случалось.</p>
72
<p>Если же приспичивает, и импортировать что-то "ну очень нужно", то можно попробовать обойтись локальным импортом:</p>
72
<p>Если же приспичивает, и импортировать что-то "ну очень нужно", то можно попробовать обойтись локальным импортом:</p>
73
<p>Да, это костыль. Но иногда полезный. В идеале - до ближайшего большого рефакторинга. Поэтому настраивайте linter на ловлю локальных импортов и стремитесь убирать такие костыли хоть когда-нибудь!</p>
73
<p>Да, это костыль. Но иногда полезный. В идеале - до ближайшего большого рефакторинга. Поэтому настраивайте linter на ловлю локальных импортов и стремитесь убирать такие костыли хоть когда-нибудь!</p>
74
<h2>Поиск пакетов и модулей</h2>
74
<h2>Поиск пакетов и модулей</h2>
75
<p>Пайтон ищет модули и пакеты в директориях, во время исполнения перечисленных в списке sys.path - по порядку от первого пути к последнему.</p>
75
<p>Пайтон ищет модули и пакеты в директориях, во время исполнения перечисленных в списке sys.path - по порядку от первого пути к последнему.</p>
76
<p>В этом списке пути до стандартных библиотек обычно расположены раньше, чем директории со сторонними пакетами, чтобы нельзя было случайно заменить стандартный пакет сторонним (помним: кто первый, того и тапки - среди нескольких с одинаковыми именами загружается первый попавшийся пакет).</p>
76
<p>В этом списке пути до стандартных библиотек обычно расположены раньше, чем директории со сторонними пакетами, чтобы нельзя было случайно заменить стандартный пакет сторонним (помним: кто первый, того и тапки - среди нескольких с одинаковыми именами загружается первый попавшийся пакет).</p>
77
<p>В списке путей (обычно в начале) присутствует и путь '', означающий текущую директорию. Это, в свою очередь, означает, что модули и пакет в текущем проекте имеют больший приоритет.</p>
77
<p>В списке путей (обычно в начале) присутствует и путь '', означающий текущую директорию. Это, в свою очередь, означает, что модули и пакет в текущем проекте имеют больший приоритет.</p>
78
<p>Обычно пути трогать не нужно, всё вполне нормально "работает само". Но если очень хочется, то путей у вас несколько:</p>
78
<p>Обычно пути трогать не нужно, всё вполне нормально "работает само". Но если очень хочется, то путей у вас несколько:</p>
79
<ol><li>Использовать переменную окружения PYTHONPATH (значение - строка с путями, разделёнными символом :),</li>
79
<ol><li>Использовать переменную окружения PYTHONPATH (значение - строка с путями, разделёнными символом :),</li>
80
<li>Во время исполнения изменить sys.path.</li>
80
<li>Во время исполнения изменить sys.path.</li>
81
</ol><p>Первый способ - простой и понятный. Не сложнее добавления пути до исполняемых файлов в PATH (даже синтаксис тот же).</p>
81
</ol><p>Первый способ - простой и понятный. Не сложнее добавления пути до исполняемых файлов в PATH (даже синтаксис тот же).</p>
82
<p>Второй способ - сложный и требующий внимательности. Дело в том, что sys.path нужно изменять максимально рано - где-нибудь в точке входа. Если не торопиться менять sys.path, то что-то уже может успеть загрузиться до того, как вы перестроите пути для поиска пакетов. А ведь эта загрузка может произойти в другом потоке исполнения! Отлаживать проблемы с очерёдностью загрузки модулей сложно. Лучше просто их не создавать.</p>
82
<p>Второй способ - сложный и требующий внимательности. Дело в том, что sys.path нужно изменять максимально рано - где-нибудь в точке входа. Если не торопиться менять sys.path, то что-то уже может успеть загрузиться до того, как вы перестроите пути для поиска пакетов. А ведь эта загрузка может произойти в другом потоке исполнения! Отлаживать проблемы с очерёдностью загрузки модулей сложно. Лучше просто их не создавать.</p>
83
<p>Кстати, когда вы используете виртуальные окружения, sys.path будет содержать пути до локальных копий стандартных библиотек. Именно это позволяет виртуальному окружению быть самодостаточным (работать на любой машине с подходящей ОС - даже без установленного в систему Python!).</p>
83
<p>Кстати, когда вы используете виртуальные окружения, sys.path будет содержать пути до локальных копий стандартных библиотек. Именно это позволяет виртуальному окружению быть самодостаточным (работать на любой машине с подходящей ОС - даже без установленного в систему Python!).</p>
84
<h2>Что не было раскрыто?</h2>
84
<h2>Что не было раскрыто?</h2>
85
<p>Я специально не стал рассказывать про</p>
85
<p>Я специально не стал рассказывать про</p>
86
<ul><li>создание модулей и пакетов на лету (без использования файлов исходников);</li>
86
<ul><li>создание модулей и пакетов на лету (без использования файлов исходников);</li>
87
<li>загрузку модулей не с диска, а из других источников;</li>
87
<li>загрузку модулей не с диска, а из других источников;</li>
88
<li>расширение подсистемы импортирования с целью загрузки в виде объектов-модулей чего-то, не являющегося кодом вовсе (XML, CSV, JSON).</li>
88
<li>расширение подсистемы импортирования с целью загрузки в виде объектов-модулей чего-то, не являющегося кодом вовсе (XML, CSV, JSON).</li>
89
</ul><p>Темы эти насколько интересны, настолько и велики. На наше счастье, самим разбираться в такой тонкой и сложной машинерии приходится редко. Мы просто пользуемся готовыми магическими артефактами, а зачаровывают их другие :) Если же вы захотите научиться магии, документация вам в руки.</p>
89
</ul><p>Темы эти насколько интересны, настолько и велики. На наше счастье, самим разбираться в такой тонкой и сложной машинерии приходится редко. Мы просто пользуемся готовыми магическими артефактами, а зачаровывают их другие :) Если же вы захотите научиться магии, документация вам в руки.</p>