#python #python-import
Вопрос:
Проблема:
pkg/
__init__.py
sub1.py
sub2.py
$ cat pkg/__init__.py
from .sub2 import *
print("init", dir())
$ cat pkg/sub1.py
from .sub2 import *
print("sub1", dir())
$ cat pkg/sub2.py
def spam():
...
$ python -c "import pkg"
init [... 'spam', 'sub2']
$ python -c "import pkg.sub1"
init [... 'spam', 'sub2']
sub1 [... 'spam']
Обратите внимание , как sub2
это в пространстве pkg
имен, хотя на самом деле я его не импортирую. Я бы ожидал, что будут импортированы только имена внутри sub2
. Почему это не так? Я вижу, что это как-то связано с импортом пакета по сравнению с импортом модуля, потому что:
$ python -c "import pkg.__init__"
init [... 'spam', 'sub2']
init [... 'spam']
Это также, кажется, сбивает с толку mypy
; Я редактирую __init__.py
, чтобы явно получить доступ sub2
:
$ cat pkg/__init__.py
from .sub2 import *
print(sub2)
Затем бег mypy pkg
дает:
pkg/__init__.py:2: error: Name "sub2" is not defined
Found 1 error in 1 file (checked 3 source files)
Почему это происходит? Является ли это задокументированной функцией? Я должен отметить, что эта «функция» используется в исходном коде Cpython; проверьте, например, Lib/asyncio/__init__.py
.
Ответ №1:
Это немного причуда подмодулей, но это задокументированное поведение:
Когда подмодуль загружается с использованием любого механизма (например
importlib
, API, операторовimport
илиimport-from
или встроенных__import__()
), привязка помещается в пространство имен родительского модуля к объекту подмодуля. Например, если в пакетеspam
есть подмодульfoo
, после импортаspam.foo
уspam
него будет атрибутfoo
, привязанный к подмодулю.
…
Учитывая знакомые правила привязки имен Python, это может показаться удивительным, но на самом деле это фундаментальная особенность системы импорта. Инвариантное удержание заключается в том, что если у вас есть
sys.modules['spam']
иsys.modules['spam.foo']
(как и после вышеупомянутого импорта), последнее должно отображаться как атрибут foo первого.