из <module>import ... in __init__.py делает имя модуля видимым? - PullRequest
15 голосов
/ 18 мая 2011

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

Файл 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 1017 * видимым . Согласно документации Python , это не должно иметь место:

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

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

Ответы [ 2 ]

6 голосов
/ 19 мая 2011

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

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

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

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

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

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

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


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

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

  1. Убедитесь, что P импортирован, или импортируйте его другим способом. (Этот шаг повторяется для обработки «import A.B.C.D».)
  2. Выполните S.py, чтобы получить объект модуля. (Пропуск деталей файлов .pyc и т. Д.)
  3. Хранить объект модуля в sys.modules ["P.S"].
  4. setattr(sys.modules["P"], "S", sys.modules["P.S"])
  5. Если этот импорт имел форму «import P.S», свяжите «P» в локальной области видимости.
0 голосов
/ 18 мая 2011

это потому, что __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
...