Из <module> import... в __init__.py делает имя модуля видимым?

Возьмем следующий пример кода:

Файл package1/__init__.py:

from moduleB import foo
print moduleB.__name__

Файл package1/moduleB.py:

def foo(): pass

Затем из текущего каталога:

>>> import package1
package1.moduleB

Этот код работает в CPython. Меня удивляет, что оператор from ... import in __init__.py делает имя moduleB видимым. Согласно документации Python, это не должно быть:

Форма from не связывает имя модуля

Может кто-нибудь объяснить, почему CPython работает именно так? Есть ли какая-либо документация, описывающая это подробно?

Ответы

Ответ 1

Документация вводит вас в заблуждение, поскольку она написана для описания более распространенного случая импорта модуля из-за пределов родительского пакета, содержащего его.

Например, используя "из примерного подмодуля импорта" в моем собственном коде, где "пример" - это некоторая сторонняя библиотека, полностью не связанная с моим собственным кодом, не связывает имя "пример". Он по-прежнему импортирует модули example/__ init__.py и example/submodule.py, создает два объекта модуля и присваивает example.submodule второму объекту модуля.

Но "from..import" имен из подмодуля должен установить атрибут подмодуля на родительском объекте пакета. Подумайте, не получилось ли это:

  • package/__ init__.py выполняется при импортировании пакета.

  • То, что __init__ делает "из имени импорта подмодуля".

  • В какой-то момент позже другой совершенно другой код выполняет "import package.submodule".

На шаге 3 либо sys.modules [ "package.submodule" ] не существует, и в этом случае его загрузка снова даст вам два разных объекта модуля в разных областях; или sys.modules [ "package.submodule" ] будет существовать, но "подмодуль" не будет атрибутом родительского объекта пакета (sys.modules [ "package" ]), а "import package.submodule" ничего не сделает. Однако, если он ничего не делает, код с использованием импорта не может получить доступ к подмодулю в качестве атрибута пакета!


Теоретически, как можно импортировать работы подмодуля, если остальная часть механизма импорта была изменена, чтобы соответствовать.

Если вам просто нужно знать, что будет делать импорт субмодуля S из пакета P, то в двух словах:

  • Убедитесь, что P импортирован или импортирован в противном случае. (Этот шаг рекурсирует для обработки "import A.B.C.D".)
  • Выполните S.py для получения объекта модуля. (Пропуск информации о файлах .pyc и т.д.).
  • Сохранить объект модуля в sys.modules [ "P.S" ].
  • setattr(sys.modules["P"], "S", sys.modules["P.S"])
  • Если этот импорт имеет форму "import P.S", привяжите "P" в локальной области.

Ответ 2

это потому, что __init__.py представляет себя как объект модуля package1 во время выполнения, поэтому каждый .py файл будет определен как подмодуль. и переписать __all__ не имеет никакого смысла. вы можете сделать другой файл, например example.py, и заполнить его тем же кодом в __init__.py, и он поднимет NameError.

Я думаю, что среда исполнения CPython принимает специальный алгоритм, когда __init__.py ищет переменные, отличные от других файлов python, может быть примерно так:

looking for variable named "moduleB"
if not found:
   if __file__ == '__init__.py': #dont raise NameError, looking for file named moduleB.py
         if current dir contains file named "moduleB.py":
                      import moduleB
         else:
                    raise namerror