Ответ 1
Из документации системы импорта:
Когда подмодуль загружается с использованием любого механизма (например,
importlib
API, операторыimport
илиimport-from
или встроенные__import__()
) привязка помещается в пространство имен родительских модулей в подмодуль объект. Например, если пакетspam
имеет подмодульfoo
, после importspam.foo
,spam
будет иметь атрибутfoo
, который связанных с подмодулем. Допустим, у вас есть следующий каталог Структура:spam/ __init__.py foo.py bar.py
и
spam/__init__.py
имеют в нем следующие строки:from .foo import Foo from .bar import Bar
тогда выполнение следующего значения привязывает имя к
foo
иbar
в модульspam
:>>> import spam >>> spam.foo <module 'spam.foo' from '/tmp/imports/spam/foo.py'> >>> spam.bar <module 'spam.bar' from '/tmp/imports/spam/bar.py'>
Учитывая знакомые правила привязки имен Pythons, это может показаться неожиданным, но на самом деле это фундаментальная особенность системы импорта. инвариантный холдинг состоит в том, что если у вас есть
sys.modules['spam']
иsys.modules['spam.foo']
(как и после указанного импорта), последний должен отображаться как атрибутfoo
первого.
Если вы выполняете from testapp.api.utils import x
, оператор импорта не загружает utils
в локальное пространство имен. Тем не менее, импортная техника загрузит utils
в пространство имен testapp.api
, чтобы сделать дальнейшие импортные операции правильными. Просто случается, что в вашем случае testapp.api
также является локальным пространством имен, поэтому вы получаете сюрприз.