В операторе import
ссылка на модуль никогда не использует поиск атрибутов. Заявления
import parent.a # as ...
и
from parent.a import ... # as ...
всегда будет искать parent.a
в пространстве имен sys.modules
, прежде чем пытаться продолжить загрузку модуля с диска.
Однако для операторов from ... import name
Python просматривает атрибуты разрешенного модуля, чтобы найти name
, прежде чем искать подмодули.
Глобальные значения модуля и атрибуты объекта модуля - это одно и то же. При импорте Python добавляет подмодули как атрибуты (например, глобальные) в родительский модуль, но вы можете перезаписывать эти атрибуты, как вы это делали в своем коде. Однако при использовании импорта с путем к модулю parent.a
атрибуты не вступают в игру.
Из раздела Подмодули справочной документации по системе импорта Python :
Когда подмодуль загружается с использованием любого механизма [...], в пространство имен родительского модуля помещается привязка к объекту подмодуля. Например, если пакет spam
имеет подмодуль foo
, после импорта spam.foo
, spam
будет иметь атрибут foo
, который связан с подмодулем.
Ваш оператор import parent.a as _a
добавляет два имени в пространство имен parent
; сначала добавляется a
, указывая на подмодуль parent.a
, а затем также устанавливается _a
, указывая на тот же объект.
Следующая строка заменяет имя a
привязкой к объекту 'some string'
.
Раздел Поиск того же подробно описывает, как Python выполняет поиск модуля при импорте:
Чтобы начать поиск, Python требуется полное имя импортируемого модуля [...].
[...]
Это имя будет использоваться на разных этапах поиска импорта, и это может быть пунктирный путь к подмодулю, например, foo.bar.baz
. В этом случае Python сначала пытается импортировать foo
, затем foo.bar
и, наконец, foo.bar.baz
. Если какой-либо из промежуточных импортеров завершается неудачно, ModuleNotFoundError
повышается.
затем далее
Первое место, проверенное при поиске импорта, - sys.modules
. Это отображение служит кешем всех модулей, которые были ранее импортированы, включая промежуточные пути. Таким образом, если foo.bar.baz
был ранее импортирован, sys.modules
будет содержать записи для foo
, foo.bar
и foo.bar.baz
. Каждый ключ будет иметь в качестве значения соответствующий объект модуля.
Во время импорта имя модуля ищется в sys.modules
, и, если оно присутствует, связанным значением является модуль, удовлетворяющий импорту, и процесс завершается. [...] Если имя модуля отсутствует, Python продолжит поиск модуля.
Поэтому при попытке импортировать parent.a
все, что имеет значение, это то, что sys.modules['parent.a']
существует. sys.modules['parent'].a
не справляются.
Только from module import ...
когда-либо будет смотреть на атрибуты. Из import
справочной документации :
Форма from
использует чуть более сложный процесс:
- найти модуль, указанный в предложении from, загрузить и при необходимости инициализировать его;
- для каждого из идентификаторов, указанных в пунктах импорта:
- проверить, есть ли у импортированного модуля атрибут с таким именем
- если нет, попытайтесь импортировать подмодуль с этим именем, а затем снова проверьте импортированный модуль на наличие этого атрибута
- [...]
Так что from parent import _a
будет работать, как и from parent import a
, и вы получите подмодуль parent.a
и объект 'some string'
соответственно.
Обратите внимание, что sys.modules
доступен для записи, если вы должны иметь import parent._a
работу, вы всегда можете просто изменить sys.modules
напрямую:
sys.modules['parent._a'] = sys.modules['parent.a'] # make parent._a an alias for parent.a
import parent._a # works now