Ответ 1
Ситуация такова: у вас есть script (run.py
), пакет test
и его подмодуль test.b
.
Всякий раз, когда вы импортируете подмодуль в Python, имя этого подмодуля автоматически сохраняется в родительском пакете. Так что, когда вы делаете import collections.abc
(или from collections.abc import Iterable
или аналогичный), пакет collections
автоматически получает атрибут abc
.
Это то, что происходит здесь. Когда вы выполните:
from b import B
имя b
автоматически загружается в test
, потому что b
является подмодулем пакета test
.
Даже если вы не делаете import b
явно, всякий раз, когда вы импортируете этот модуль, имя помещается в test
. Поскольку b
является подмодулем в test
, и он принадлежит test
.
Сторона node: ваш код не будет работать с Python 3, потому что относительный импорт удален, Чтобы заставить ваш код работать с Python 3, вам нужно написать:
from test.b import B
Этот синтаксис совершенно идентичен from b import B
, но гораздо более явный и должен помочь вам понять, что происходит.
Ссылка: справочная документация Python также объясняет это поведение и включает полезный пример, очень похожий на этот (разница заключается только в том, что используется абсолютный импорт вместо относительного импорта).
Когда подмодуль загружается с использованием любого механизма (например,
importlib
API, операторовimport
илиimport-from
или встроенного__import__()
)), привязка помещается в пространство имен родительского модуля в объект подмодуля. Например, если пакетspam
имеет подмодульfoo
, после импортаspam.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'>
Учитывая знакомые правила привязки имени Python, это может показаться удивительным, но на самом деле это фундаментальная особенность системы импорта. Инвариантный холдинг состоит в том, что если у вас есть
sys.modules['spam']
иsys.modules['spam.foo']
(как и после указанного импорта), последний должен появиться как атрибутfoo
для первого.