Вопрос-Ответ

Is __init__.py not required for packages in Python 3.3+

Является __init__.py не требуется для пакетов в Python 3.3+

Я использую Python 3.5.1. Я прочитал документ и раздел пакета здесь: https://docs.python.org/3/tutorial/modules.html#packages

Теперь у меня есть следующая структура:

/home/wujek/Playground/a/b/module.py

module.py:

class Foo:
def __init__(self):
print('initializing Foo')

Теперь, находясь в /home/wujek/Playground:

~/Playground $ python3
>>> import a.b.module
>>> a.b.module.Foo()
initializing Foo
<a.b.module.Foo object at 0x100a8f0b8>

Аналогично, теперь в home, суперпапке Playground:

~ $ PYTHONPATH=Playground python3
>>> import a.b.module
>>> a.b.module.Foo()
initializing Foo
<a.b.module.Foo object at 0x10a5fee10>

На самом деле, я могу делать все, что угодно:

~ $ PYTHONPATH=Playground python3
>>> import a
>>> import a.b
>>> import Playground.a.b

Почему это работает? Я думал, что должны быть __init__.py файлы (пустые подойдут) в обоих a и b для module.py возможности импорта, когда путь Python указывает на Playground папку?

Похоже, это изменилось по сравнению с Python 2.7:

~ $ PYTHONPATH=Playground python
>>> import a
ImportError: No module named a
>>> import a.b
ImportError: No module named a.b
>>> import a.b.module
ImportError: No module named a.b.module

С __init__.py в обоих ~/Playground/a и ~/Playground/a/b это работает нормально.

Переведено автоматически
Ответ 1

Обзор

Ответ @Mike правильный, но слишком неточный. Это правда, что Python 3.3 + поддерживает неявные пакеты пространства имен, что позволяет создавать пакет без __init__.py файла. Это называется пакетом пространства имен в отличие от обычного пакета, в котором есть __init__.py файл (пустой или не пустой).

Однако создавать пакет пространства имен следует ТОЛЬКО в том случае, если в этом есть необходимость. Для большинства вариантов использования и для разработчиков это неприменимо, поэтому вы должны придерживаться ПУСТЫХ __init__.py файлов в любом случае.

Пример использования пакета Namespace

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

google_pubsub/              <- Package 1
google/ <- Namespace package (there is no __init__.py)
cloud/ <- Namespace package (there is no __init__.py)
pubsub/ <- Regular package (with __init__.py)
__init__.py <- Required to make the package a regular package
foo.py

google_storage/ <- Package 2
google/ <- Namespace package (there is no __init__.py)
cloud/ <- Namespace package (there is no __init__.py)
storage/ <- Regular package (with __init__.py)
__init__.py <- Required to make the package a regular package
bar.py

google_pubsub и google_storage являются отдельными пакетами, но они используют одно и то же пространство имен google/cloud. Чтобы использовать одно и то же пространство имен, требуется сделать каждый каталог общего пути пакетом пространства имен, т.е. google/ и cloud/. Это должен быть единственный вариант использования для создания пакетов пространства имен, в противном случае в нем нет необходимости.

Крайне важно, чтобы в каталогах __init__py и google не было google/cloud файлов, чтобы оба каталога можно было интерпретировать как пакеты пространства имен. В Python 3.3+ любой каталог в sys.path с именем, совпадающим с именем искомого пакета, будет распознан как добавляющие модули и подпакеты к этому пакету. В результате, когда вы импортируете оба из google_pubsub и google_storage, интерпретатор Python сможет их найти.

Это отличается от обычных пакетов, которые являются автономными, что означает, что все части находятся в одной иерархии каталогов. При импорте пакета интерпретатор Python обнаруживает подкаталог в sys.path с __init__.py файлом, тогда он создаст пакет с одним каталогом, содержащий только модули из этого каталога, вместо того, чтобы находить все подкаталоги с соответствующими именами за пределами этого каталога. Это прекрасно подходит для пакетов, которые не хотят совместно использовать пространство имен. Я настоятельно рекомендую взглянуть на ловушки для неосторожных в системе импорта Python, чтобы лучше понять, как импорт Python работает с обычным пакетом и пакетом пространства имен и каких __init__.py ловушек следует остерегаться.

Краткие сведения


  • Пропускайте __init__.py файлы только в том случае, если вы хотите создать пакеты пространства имен. Создавайте пакеты namespace только в том случае, если у вас есть разные библиотеки, которые находятся в разных расположениях, и вы хотите, чтобы каждая из них добавляла подпакет в родительский пакет, то есть пакет namespace.

  • Продолжайте добавлять empty __init__.py в свои каталоги, потому что в 99% случаев вы просто хотите создавать обычные пакеты. Кроме того, существующие инструменты Python, такие как mypy и pytest требуют пустых __init__.py файлов для соответствующей интерпретации структуры кода. Это может привести к странным ошибкам, если не выполнять их с осторожностью.

Ресурсы

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

Ответ 2

В Python 3.3+ есть неявные пространства имен Packages, которые позволяют создавать пакеты без __init__.py файла.


Разрешение неявных пакетов пространства имен означает, что требование о предоставлении __init__.py файла может быть полностью отменено и затронуто ... .


Старый способ с __init__.py файлами по-прежнему работает как в Python 2.

Ответ 3

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


Пакеты распознаются только в том случае, если они включают __init__.py файл


UPD: если вы хотите использовать неявные пакеты пространства имен без __init__.py вам просто нужно использовать find_namespace_packages() вместо этого

Документы

Ответ 4

Я бы сказал, что следует опустить __init__.py только если вы хотите иметь неявный пакет пространства имен. Если вы не знаете, что это значит, вам, вероятно, это не нужно, и поэтому вам следует продолжать использовать __init__.py даже в Python 3.

python python-3.x