Для чего нужен __init__.py в Python?
Оглавление
- Вкратце: __init__.py Объявляет папку как обычный пакет Python
- Что происходит, когда я добавляю код в __init__.py?
- Что произойдет, если я не Добавить файл __init__.py ?
- Какой код я должен ввести в __init__.py?
- Когда выполняется __init__.py?
- Как __init__.py Используется в практическом примере?
- Могу ли я использовать __init__.py для управления тем, что экспортируется из пакета?
- Является ли __init__.py Верхнего уровня Отличным от Того, который содержится во вложенном пакете?
- Заключение
- Часто задаваемые вопросы
Специальный файл Python __init__.py помечает каталог как обычный пакет Python и позволяет импортировать его модули. Этот файл запускается автоматически при первом импорте содержащего его пакета. Вы можете использовать его для инициализации переменных уровня пакета, определения функций или классов и структурирования пространства имен пакета, понятного пользователям.
К концу этого урока вы поймете, что:
- Каталог без
__init__.pyфайла становится пакетом пространства имен, который ведет себя иначе, чем обычный пакет, и это может привести к замедлению импорта. - Вы можете использовать
__init__.pyдля явного определения общедоступного API пакета путем импорта определенных модулей или функций в пространство имен пакета. - Соглашение Python об использовании начальных знаков подчеркивания помогает указать пользователям, какие объекты предназначены как закрытые, хотя это соглашение все еще может быть обойденным.
- Код внутри
__init__.pyвыполняется только один раз во время первого импорта, даже если вы запускаете инструкцию import несколько раз.
Понимание того, как эффективно использовать __init__.py, поможет вам структурировать ваши пакеты Python понятным и доступным в обслуживании способом, улучшая удобство использования и управление пространством имен.
Примечание: Имя файла __init__.py трудно произносится. Большинству людей проще называть это dunder-init.
Возможно, вы видели файлы с именем __init__.py, разбросанные по крупным проектам на Python, и задавались вопросом, для чего именно они предназначены. Или, возможно, вы сами использовали файлы __init__.py, не имея четкого представления о том, зачем они нужны и как использовать их возможности. Возможно, вы также заметили, что ваш код на Python иногда работает, даже если вы забываете добавить __init__.py в свои пакеты.
Итак, для чего нужен __init__.py?
Короче говоря: __init__.py объявляет папку как обычный пакет Python
Специальный файл __init__.py служит маркером, указывающим на то, что содержащийся в нем каталог является обычным пакетом. Когда люди говорят о пакете в Python, они обычно имеют в виду обычный пакет. Файл __init__.py является исходным файлом Python, что означает, что он также является модулем.
Если вы хотите ознакомиться с терминами модуль и пакет и с тем, как они используются в Python, тогда разверните раздел "сворачиваемый" ниже:
В Python термины модуль и пакет описывают модули, содержащие код:
| Модуль | Пакет | |
|---|---|---|
| Определение | Отдельный файл Python, содержащий код | Каталог, содержащий один или несколько модулей Python |
| Именование | Имя файла без расширения .py |
Имя каталога |
| Содержимое | Функции, классы и переменные | Модули и, возможно, Подпакеты |
| Назначение | Организация небольших проектов, повторное использование простого кода | Организация крупных проектов, повторное использование кода |
| Импорт | Прямой импорт (import module) |
Импорт пакета или его модулей (import package.module) |
Как вы можете видеть, и модули, и пакеты помогают организовать код на Python, причем модули представляют собой отдельные файлы, а пакеты - каталоги, которые могут группировать несколько модулей.
Наличие __init__.py заставляет загрузчик Python идентифицировать содержащийся в нем каталог как обычный пакет. Это означает, что вы можете импортировать весь каталог, определенные модули внутри него или даже отдельные функции, переменные и классы из этих модулей, используя имя пакета.
Вы также можете импортировать эти объекты кода в другие модули, что позволяет многократно использовать ваш код различными гибкими способами. Вскоре вы увидите несколько примеров этого.
Как уже упоминалось, __init__.py может быть пустым файлом, и в этом случае он служит только для идентификации пакета.
Что происходит, когда я добавляю код в __init__.py?
Если __init__.py содержит код, то этот код будет выполнен при первом импорте пакета.
Код в __init__.py может выполнять множество полезных задач. Например, он может импортировать другие модули или пакеты и определять свои собственные функции или данные. Когда пакет импортируется, весь его собственный код, а также все, что он сам импортировал, становится доступным для импортирующего модуля.
Вы начнете с простого примера, основанного на одном пакете. Вот настройка:
tools_project/
│
└── tools/
└── __init__.py
Итак, ваш проект называется tools_project. У него есть пакет с именем tools. Файл tools/__init__.py мог бы быть полностью пустым, но здесь он содержит несколько строк кода:
tools_project/tools/__init__.py
__version__ = "1.0.0"
magic_number = 42
Установив эти файлы, запустите сеанс REPL в каталоге tools_project и посмотрите, что произойдет при импорте и использовании пакета tools:
1>>> import tools
2>>> tools.__version__
3'1.0.0'
4>>> tools.magic_number
542
Когда вы выполнили import tools в строке 1, файл __init__.py в каталоге tools был выполнен неявно. Это сделало имена в tools/__init__.py доступными через префикс tools. Как правило, вы можете использовать __init__.py для объявления переменных, относящихся к импортируемому пакету, таких как версия пакета или константа для всего пакета.
Примечание: Код в __init__.py выполняется только один раз, даже если вы импортируете пакет несколько раз. Позже вы увидите это более подробно.
В оставшейся части этого руководства вы узнаете больше о том, когда, почему и как вы можете захотеть использовать __init__.py.
Что произойдет, если я не добавлю файл __init__.py?
До сих пор вы изучали обычные пакеты, но Python также поддерживает пакеты другого типа, известные как пакеты пространства имен. У пакета пространства имен нет файла __init__.py. Вы все еще можете импортировать его, при условии, что Python сможет найти его с помощью sys.path. Если вы не добавите файл __init__.py в каталог, он будет рассматриваться как пакет пространства имен.
Как обычные пакеты, так и пакеты пространства имен определяют пространства имен. Разница заключается в том, что пакет пространства имен не ограничен одним каталогом — содержимое пакета идентифицируется по префиксу пространства имен, а не по физическому каталогу.
Примечание: Пакеты пространств имен существуют уже много лет, но раньше они требовали некоторой дополнительной настройки. С момента принятия PEP 420 в Python 3.3 в языке было неявных пакетов пространства имен, идентифицируемых только по отсутствию __init__.py. Это облегчает их создание намеренно, но также и случайно!
Напомним, что пространство имен - это соглашение для различения различных объектов кода, которые могут иметь одинаковое базовое имя, независимо от того, является ли это именем переменной, функция, класс или что-нибудь еще. Используя разные префиксы, вы можете избежать путаницы.
Например, в большом объеме кода у вас вполне могут быть два разных модуля с именами utils. Предположим, что один из них содержит финансовые утилиты, а другой - утилиты расчета заработной платы. Размещая их в разных пространствах имен, называемых finance и payroll, вы позволяете Python различать их, даже если они имеют одно и то же базовое имя. Это связано с тем, что Python рассматривает finance.utils и payroll.utils как совершенно разные объекты кода.
Вы можете легко добавить пакет пространства имен в свой демонстрационный проект. Просто добавьте новый пустой каталог ниже tools_project:
tools_project/
│
├── tools/
│ └── __init__.py
│
└── tools_ns/
Напомним, что и модули, и пакеты должны иметь допустимые идентификаторы Python. Следовательно, ваше новое имя каталога содержит символ подчеркивания (_) вместо, скажем, дефиса (-).
Теперь в каталоге tools_project используйте REPL, чтобы убедиться, что вы можете импортировать и этот пакет. Каждый пакет Python имеет атрибут .__path__. Обратите внимание, что tools_ns.__path__ выглядит немного иначе, чем tools.__path__:
>>> import tools
>>> tools.__path__
['/tools_project/tools']
>>> import tools_ns
>>> tools_ns.__path__
_NamespacePath(['/tools_project/tools_ns'])
Python находит tools_ns в текущем каталоге, отмечает, что в нем нет __init__.py, и импортирует его как пакет пространства имен.
Хотя tools_ns был успешно импортирован, его атрибут .__path__ указывает на то, что это пакет из пространства имен, а не обычный пакет.
Примечание: Среда выполнения Python поддерживает список каталогов с именем sys.path, которые она изначально использует для загрузки своих собственных стандартных библиотек, а затем для поиска любых импортных файлов, объявленных в вашем коде. В списке указаны пути ко всем установленным вами библиотекам, а также текущий каталог запущенного скрипта.
Вы можете добавить дополнительные каталоги к sys.path, либо включив их в переменную окружения PYTHONPATH, либо изменив sys.path во время выполнения. Однако обычно в этом нет необходимости, поскольку ваш локально разработанный код будет найден в собственном каталоге скрипта и его подкаталогах. Вот как Python находит tools и tools_ns в этом примере.
Пакеты пространств имен полезны для объединения нескольких кодовых баз в единую структуру именования. Например, корпорация, имеющая несколько сайтов разработки, может захотеть объединить свои различные вклады в рамках одного пространства имен.
Например, компания под названием MegaCorp Inc. может использовать пространство имен com.megacorp. Вместо того чтобы заставлять пользователей загружать весь код из своего пространства имен в одном пакете, они могут публиковать отдельные библиотеки меньшего размера, которые имеют одинаковый префикс, например com.megacorp.finance и com.megacorp.sales.
Если MegaCorp опубликует их в виде отдельных пакетов, их можно будет поддерживать, распространять и использовать по отдельности. Этот подход может быть применен к любой крупной программной экосистеме или продукту, где логическое разделение и модульность являются ключевыми.
Пакеты пространств имен, безусловно, имеют свое применение, но у них также есть несколько предостережений. Один из них заключается в том, что при импорте пакета пространства имен интерпретатор должен выполнить поиск по всему sys.path, чтобы убедиться, что он нашел все файлы, входящие в пространство имен. Это приводит к замедлению импорта. Кроме того, поскольку полное пространство имен может быть собрано только во время выполнения, любые коллизии имен не обнаруживаются до тех пор.
Если вы не уверены, что хотите создать пакет с пространством имен, вам следует выбрать обычные пакеты, включив файл __init__.py в каждый пакет вашего проекта. Это приведет к более эффективному и предсказуемому импорту.
Остальная часть этого руководства будет посвящена обычным пакетам.
Какой код я должен ввести __init__.py?
Нет необходимости включать какой-либо исполняемый код внутрь __init__.py. Как вы уже узнали, обычно в каталоге создается пустой файл __init__.py, который служит маркером, указывающим на то, что этот каталог следует рассматривать как обычный пакет.
Однако __init__.py также может содержать функции, классы, данные и инструкции абсолютного или относительного импорта.
Это может быть полезно для импорта имен из подмодулей, чтобы структурировать понятное пространство имен для пользователей пакета верхнего уровня. Любые имена, которые вы импортируете в пакет, становятся частью его пространства имен верхнего уровня, и на них может более удобно ссылаться клиентский код.
__init__.py это хорошее место для определения полезных функций, констант и переменных, которые применяются ко всему пакету.
Таким образом, вы можете использовать файл __init__.py для:
- Импортируйте другие модули, необходимые для выполнения кода внутри самого
__init__.py - Определите переменные или функции на уровне пакета
- Настройка конфигурации или параметров для пакета
- Включить документацию или определить метаданные для пакета
- Предоставить упрощенное и согласованное пространство имен для пользователей пакетов
Теперь, когда вы знаете, какой код вы, возможно, захотите включить в __init__.py, вам может быть интересно, когда Python выполнит код из этого файла.
Когда выполняется __init__.py?
Файл __init__.py выполняется при первом импорте его пакета. Это произойдет:
- Если вы импортируете пакет явно
- Если вы импортируете модуль внутри пакета
- Если вы импортируете подпакет пакета или модуль внутри этого подпакета
Файл __init__.py начинает выполняться, как только интерпретатор встречает соответствующую инструкцию import. Любые вложенные импортные операции в файле также будут выполняться по мере их появления.
Как __init__.py Используется в практическом примере?
Вот конкретный пример того, как использовать __init__.py. Предположим, вы запускаете новый проект под названием media_project с собственным корневым каталогом. Ниже находится каталог с именем mediatools, содержащий свой собственный __init__.py, определяющий пакет mediatools и пространство имен. В этот пакет можно включить подпакеты с именами audio и graphics. Вот схема размещения:
media_project/
│
└── mediatools/
│
├── audio/
│ ├── __init__.py
│ └── utils.py
│
├── graphics/
│ ├── __init__.py
│ └── utils.py
│
└── __init__.py
В media_project вы создали пакет mediatools, который содержит два подпакета: mediatools/audio и mediatools/graphics. Модуль mediatools/audio/utils.py содержит звуковые служебные функции, объявления констант и тому подобное, в то время как mediatools/graphics/utils.py играет аналогичную роль в графической части вашего проекта.
Обратите внимание, что у каждого пакета есть свой собственный __init__.py, поэтому это обычные пакеты. Для целей этого примера вы заполните модуль mediatools/audio/utils.py следующим образом:
mediatools/audio/utils.py
1print(f"Importing {__name__}")
2
3def wobbulate():
4 print("Wibble wobble")
5
6def enhance():
7 print("Enhancing audio")
8
9_secret_password = "Alakazam!"
10__top_secret_password = "!mazakalA"
С этого момента вы будете добавлять строку типа строка 1 в каждый файл Python. Таким образом, вы будете точно знать, когда будет импортирован этот пакет или модуль.
Две переменные _secret_password и __top_secret_password в строках 9 и 10 называются непубличными переменными. Они не предназначены для использования вне их собственного модуля. Вскоре вы узнаете, что это означает на практике.
Вы также добавите некоторый код в mediatools/graphics/utils.py:
mediatools/graphics/utils.py
print(f"Importing {__name__}")
def solarize():
print("Solarizing")
def enhance():
print("Enhancing graphics")
indiana_pi = 3.2
Таким образом, пакеты audio и graphics содержат по модулю с именем utils.py, и каждый из них содержит вызов для отладки к print() вверху, где будет отображаться название модуля, некоторые определения функций и объявления переменных.
Например, модуль audio.utils содержит функцию wobbulate(). Если бы у вас была основная программа в каталоге media_project, она могла бы организовать вызов этой функции различными способами, например, путем импорта модуля с полным префиксом пространства имен:
import mediatools.audio.utils
mediatools.audio.utils.wobbulate()
Или импортировав только название модуля:
from mediatools.audio import utils
utils.wobbulate()
Или импортировав имя функции вообще без префикса:
from mediatools.audio.utils import wobbulate
wobbulate()
Затем добавьте свой демонстрационный вызов с print() на mediatools/__init__.py, чтобы вы знали, когда он будет выполнен:
mediatools/__init__.py
print(f"Importing {__name__}")
Добавьте ту же строку в graphics/__init__.py:
mediatools/graphics/__init__.py
print(f"Importing {__name__}")
А также для audio/__init__.py:
mediatools/audio/__init__.py
print(f"Importing {__name__}")
Настроив эти отладочные вызовы для print(), вы сможете наблюдать, когда выполняется код в каждом __init__.py. Это происходит при импорте соответствующего пакета. Вы можете увидеть это в действии, запустив новый сеанс REPL:
>>> from mediatools.audio import utils as audio_utils
Importing mediatools
Importing mediatools.audio
Importing mediatools.audio.utils
>>> from mediatools.graphics import utils as graphics_utils
Importing mediatools.graphics
Importing mediatools.graphics.utils
>>> audio_utils.wobbulate()
Wibble wobble
>>> graphics_utils.solarize()
Solarizing
>>> graphics_utils.indiana_pi
3.2
В этом коде вы выбрали префикс audio_utils, чтобы указать, что функция поступает из модуля utils в пакете audio. Аналогично, префикс graphics_utils идентифицирует функцию из модуля utils в пакете graphics.
Несмотря на то, что в ваших инструкциях import явно упоминаются только два модуля utils, ваши вызовы print() показывают, что код в mediatools.__init__.py, mediatools.audio.__init__.py, и mediatools.graphics.__init__.py был выполнен неявно. Итак, первые три строки, напечатанные на вашей консоли после импорта модуля utils из mediatools.audio, взяты из следующих файлов:
mediatools/__init__.pyотпечатки пальцевImporting mediatoolsmediatools/audio/__init__.pyотпечаткиImporting mediatools.audiomediatools/audio/utils.pyотпечаткиImporting mediatools.audio.utils
Обратите внимание, что код в mediatools.__init__.py был выполнен только один раз, хотя mediatools является родительским пакетом для обоих импортированных модулей. Сейчас вы поймете, почему это происходит.
Вы подтвердили, что код в файле __init__.py выполняется автоматически при импорте его пакета или чего-либо внутри него. Хотя этот код может выполнять практически все, что угодно, его обычной целью является инициализация пакета и определение любых атрибутов, функций или данных, которые являются глобальными для пакета. Он также может выполнять импорт либо для собственного внутреннего использования, либо для отображения импортированных имен в клиентском коде.
Интерпретатор Python поддерживает кэш импортированных модулей, поэтому вы не можете дважды импортировать один и тот же модуль, используя обычную инструкцию import. Python автоматически пропускает второй оператор import, и содержимое __init__.py не будет выполнено повторно. Если вы импортируете два подпакета из одного и того же родительского пакета, то родительский пакет и каждый подпакет будут импортированы ровно один раз.
Посмотрите, что происходит, когда вы запускаете новый REPL и пытаетесь загрузить audio.utils дважды:
>>> from mediatools.audio import utils
Importing mediatools
Importing mediatools.audio
Importing mediatools.audio.utils
>>> from mediatools.audio import utils as more_utils
>>> utils.enhance()
Enhancing audio
>>> more_utils.enhance()
Enhancing audio
Вторая инструкция import не выполняла никакого импорта — она только добавляла псевдоним к предыдущему import utils имени more_utils.
Примечание: Вы можете принудительно выполнить повторное выполнение файла __init__.py с динамическим импортом, используя библиотеку Python importlib:
>>> import mediatools
Importing mediatools
>>> import importlib
>>> mediatools = importlib.reload(mediatools)
Importing mediatools
Однако имейте в виду, что вы должны использовать динамический импорт и перезагрузку выборочно — только тогда, когда стандартный статический импорт Python не обеспечивает достаточной гибкости или когда вам явно требуется управление модулями во время выполнения.
Прежде чем продолжить, вы можете удалить отладочные вызовы print() из всех ваших файлов, чтобы избежать загромождения выходных данных в следующих разделах.
Могу ли я использовать __init__.py для управления тем, что экспортируется из пакета?
Краткий ответ таков: не совсем. В Python действительно есть механизмы и соглашения для контроля экспорта, но их легко обойти. Одно из таких соглашений касается имен, которые начинаются с одного или нескольких символов подчеркивания . Соглашение предписывает, что внешний код не должен ссылаться на эти имена. Используя эти “имена с подчеркиванием”, разработчик пакета может помочь пользователю пакета понять, что должно и чего не должно быть включено в общедоступный API.
На практике разработчик пакета несет ответственность за использование этого соглашения для указания того, что не предназначено для экспорта, но пользователь пакета должен соблюдать это соглашение. Это можно легко проигнорировать, как вы можете видеть в следующем фрагменте кода:
>>> import mediatools.audio.utils
>>> mediatools.audio.utils._secret_password
'Alakazam!'
>>> mediatools.audio.utils.__top_secret_password
'!mazakalA'
Python с удовольствием импортирует и печатает значения этих предположительно закрытых переменных.
Синтаксис import * соответствует условию подчеркивания. Вы можете использовать этот синтаксис для импорта содержимого модуля mediatools/audio/utils.py в новом сеансе REPL:
>>> from mediatools.audio.utils import *
>>> _secret_password
Traceback (most recent call last):
...
NameError: name '_secret_password' is not defined
>>> __top_secret_password
Traceback (most recent call last):
...
NameError: name '__top_secret_password' is not defined
При таком синтаксисе большинство имен внутри mediatools.audio.utils становятся доступными для вашей программы без префикса, но переменные с подчеркиванием не импортируются.
Примечание: Синтаксис import * часто удобен в интерактивной работе, но считается плохой практикой в крупных проектах, поскольку он стирает информацию о пространстве имен модуля. Все объекты, определенные в mediatools.audio.utils, за исключением тех, которые начинаются со знака подчеркивания, импортируются и в процессе теряют свой префикс. Они становятся частью глобального пространства имен, что увеличивает риск коллизий имен.
Чтобы увидеть эту проблему на практике, запустите новый сеанс REPL в каталоге media_project и используйте import * для своих подмодулей:
>>> from mediatools.audio.utils import *
>>> from mediatools.graphics.utils import *
>>> wobbulate()
Wibble wobble
>>> indiana_pi
3.2
>>> solarize()
Solarizing
>>> enhance()
Enhancing graphics
Интерпретатор больше не может различать две версии enhance(). Фактически, второй import * импортировал имя enhance без префикса пространства имен из graphics.utils. Это привело к сбою enhance, импортированного из audio.utils, что привело к неожиданному поведению.
Примечание: Python предоставляет специальный список с именем __all__, который, будучи определен в модуле, определяет, какие имена импортируются с помощью инструкции from module import *. Будут импортированы только имена, перечисленные в __all__.
Большинство программистов будут следовать рекомендациям и избегать import * в рабочем коде. Однако __all__ также соблюдается средствами документации при определении общедоступного API кода. IDE инструменты автозавершения также проверяют __all__, чтобы убедиться, что только предлагается общедоступное дополнение кода.
Вы можете продемонстрировать использование __all__, добавив строку в mediatools/graphics/utils.py:
mediatools/graphics/utils.py
# ...
__all__ = ["solarize", "enhance"]
При таком добавлении import * будут импортированы только две именованные функции, а переменная indiana_pi больше не будет видна:
>>> from mediatools.graphics.utils import *
>>> indiana_pi
Traceback (most recent call last):
...
NameError: name 'indiana_pi' is not defined
Хотя вы не можете запретить пользователям вашего пакета импортировать символы таким образом, который вы не планировали, вы можете структурировать свое пространство имен таким образом, чтобы помочь им понять, как должен работать код.
Например, вместо того, чтобы ожидать, что разработчик будет вникать в детали двух пакетов utils, чтобы решить, что и как импортировать, вы можете добавить пару строк в mediatools/__init__.py, чтобы упростить задачу. еще немного пояснений:
mediatools/__init__.py
from .audio.utils import enhance as audio_enhance
from .audio.utils import wobbulate
from .graphics.utils import enhance as graphics_enhance
from .graphics.utils import solarize
Здесь вы использовали некоторые относительные импортные данные, чтобы упростить представление разработчика о пакете mediatools. Это соответствует принципу разработки программного обеспечения сокрытия информации: пользователю пакета не нужно видеть кровавые подробности mediatools и его подпакетов. Им просто нужно определение интерфейса, как показано выше, чтобы помочь им использовать пакет на высоком уровне.
Теперь пользовательский код может воспользоваться преимуществами нового интерфейса:
>>> from mediatools import (
... wobbulate,
... solarize,
... audio_enhance,
... graphics_enhance
... )
>>> wobbulate()
Wibble wobble
>>> solarize()
Solarizing
>>> audio_enhance()
Enhancing audio
>>> graphics_enhance()
Enhancing graphics
При использовании этого интерфейса клиентский код лучше отделен от внутренней части пакетов mediatools. Это упрощает написание клиентского кода и его обслуживание. Обратите внимание, что ваш единственный оператор import вызвал цепную реакцию импорта: сначала родительский пакет, затем каждый подпакет со своим модулем.
Иногда вы хотите импортировать пакеты в __init__.py для внутреннего использования, но чтобы избежать загрязнения пространства имен, вы не хотите раскрывать эти имена пользователям ваша посылка.
Вот схематический __init__.py файл, который делает это для воображаемого приложения:
from datetime import date as _date
from math import gcd as _gcd
from some.thirdparty.dateutils import lunar_month as _lunar_month
def fancy_date_calculator() -> str:
today = _date.today
# Some fancy calculations using _gcd and _lunar_month
...
return "Calculated result"
Когда пользовательский код импортирует этот пакет, он увидит только одну общедоступную функцию: fancy_date_calculator(). Все остальные символы будут дополнены символами подчеркивания, что дает понять, что они не предназначены для публичного использования.
Отличается ли файл __init__.py верхнего уровня от файла в подпакете?
__init__.py в папке на верхнем уровне дерева импорта часто содержатся некоторые дополнительные метаданные о библиотеке в целом. Это может включать номер версии, URL-адрес сайта проекта и контактную информацию для разработчиков. В противном случае он ведет себя так же, как и любой другой __init__.py.
Например, предположим, что вы хотите добавить номер версии пакета. Хорошим местом для этого было бы внутри mediatools/__init__.py:
mediatools/__init__.py
__version__ = "0.1.0"
# ...
Скорее всего, вы оставите остальные __init__.py файлы пустыми, хотя ничто не мешает вам также записывать туда информацию о версии для ваших подпакетов.
Заключение
В этом руководстве вы узнали об особой роли файла Python __init__.py в определении обычных пакетов и отличии их от пакетов пространства имен.
Вы изучили, как Python выполняет код внутри __init__.py при импорте пакета, как использовать его для структурирования пространств имен и API и как эффективно обрабатывать импорт. Кроме того, вы узнали о правилах присвоения имен с подчеркиванием в Python для непубличных символов и о том, как переменная __all__ может помочь явно определить открытый интерфейс модуля.
Эффективное использование __init__.py может помочь вам создавать хорошо организованные, удобные в обслуживании и интуитивно понятные пакеты. Правильно структурируя свои пакеты, вы улучшаете их читаемость, поощряете повторное использование и упрощаете инициализацию на уровне пакетов.
В этом руководстве вы узнали, как:
- Пометьте каталоги как обычные пакеты, используя
__init__.py - Определите понятный, эксплицитный общедоступный API, используя импорт в
__init__.py - Используйте префиксы подчеркивания для обозначения непубличных символов
- контролировать то, что импортируется с
__all__переменная - Понимать, когда и как Python выполняет код в
__init__.py
В следующий раз, когда вы будете создавать файл __init__.py, вы точно поймете, как он вписывается в ваш проект, и будете готовы добавить код, который структурирует ваши пространства имен, инициализирует данные или сохраняет метаданные.
Часто задаваемые вопросы
Теперь, когда у вас есть некоторый опыт работы с __init__.py в Python, вы можете использовать вопросы и ответы, приведенные ниже, чтобы проверить свое понимание и резюмировать то, что вы узнали.
Эти часто задаваемые вопросы относятся к наиболее важным понятиям, которые вы рассмотрели в этом руководстве. Нажмите на переключатель Показывать/скрывать рядом с каждым вопросом, чтобы открыть ответ.
- Пустой файл
__init__.pyуказывает Python на то, что каталог является обычным пакетом, позволяющим импортировать из него модули. - Без
__init__.pyPython распознает каталог как пакет пространства имен, а не как обычный пакет. Пакеты пространства имен могут охватывать несколько каталогов, но их импорт обычно происходит медленнее. - Вы не можете строго контролировать то, что импортируется, но вы можете направлять пользователей, явно определяя общедоступный API в вашем файле
__init__.py. Условные обозначения Python, такие как начальное подчеркивание и переменная__all__, помогают пользователям понять, для чего вы их используете. - Python выполняет код в
__init__.pyпри первом импорте пакета или любого из его модулей. Python запускает этот код только один раз, кэшируя пакет для последующего импорта.