What happens when using mutual or circular (cyclic) imports?
Что происходит при использовании взаимного или циклического (cyclic) импорта?
Что происходит в Python, когда два модуля пытаются import друг друга? В более общем плане, что происходит, если несколько модулей пытаются import в цикле?
Если вы сделаете import foo (внутри bar.py) и import bar (внутри foo.py), это будет работать нормально. К тому времени, когда что-либо действительно запустится, оба модуля будут полностью загружены и будут иметь ссылки друг на друга.
Проблема заключается в том, что вместо этого вы делаете from foo import abc (внутри bar.py) и from bar import xyz (внутри foo.py). Потому что теперь каждый модуль требует, чтобы другой модуль уже был импортирован (чтобы имя, которое мы импортируем, существовало), прежде чем его можно будет импортировать.
Примеры рабочего циклического импорта в Python 2 и Python 3
# lib/foo.py # lib/bar.py defabc(): defxyz(): from . import bar from . import foo print(bar.xyz.__name__) print(foo.abc.__name__)
Дополнительные примеры
В приведенной выше статье не обсуждается звездообразный импорт.
Ответ 2
В прошлом году на comp.lang.python было действительно хорошее обсуждение этого вопроса. Он довольно подробно отвечает на ваш вопрос.
Импорт на самом деле довольно прост. Просто помните следующее:
'import' и 'from xxx import yyy' являются исполняемыми операторами. Они выполняются, когда запущенная программа достигает этой строки.
Если модуля нет в sys.modules, то при импорте создается новая запись модуля в sys.modules, а затем выполняется код в модуле. Он не возвращает управление вызывающему модулю до завершения выполнения.
Если модуль действительно существует в sys.modules, то импорт просто возвращает этот модуль независимо от того, завершил он выполнение или нет. Именно по этой причине циклический импорт может возвращать модули, которые кажутся частично пустыми.
Наконец, выполняющийся скрипт выполняется в модуле с именем __main__, импорт скрипта под его собственным именем создаст новый модуль, не связанный с __main__.
Соберите все это вместе, и вы не должны столкнуться с какими-либо сюрпризами при импорте модулей.
Ответ 3
Циклический импорт завершается, но вам нужно быть осторожным, чтобы не использовать циклически импортируемые модули во время инициализации модуля.
$ python a.py a in b imported: False b in a in b imported: True a out b out a out
При втором импорте b.py (во втором a in) интерпретатор Python не импортирует b снова, потому что он уже существует в модуле dict .
Если вы попытаетесь получить доступ b.x из a во время инициализации модуля, вы получите AttributeError.
Добавьте следующую строку в a.py:
print b.x
Тогда результат будет следующим:
$ python a.py a in b imported: False b in a in b imported: True a out Traceback (most recent call last): File "a.py", line 4, in <module> import b File "/home/shlomme/tmp/x/b.py", line 2, in <module> import a File "/home/shlomme/tmp/x/a.py", line 7, in <module> print b.x AttributeError: 'module'object has no attribute 'x'
Это связано с тем, что модули выполняются при импорте и на момент b.x обращения к строке x = 3 еще не выполнена, что произойдет только после b out.
Ответ 4
Как описано в других ответах, этот шаблон приемлем в python:
defdostuff(self): from foo import bar ...
Это позволит избежать выполнения инструкции import при импорте файла другими модулями. Сбой произойдет только при наличии логической циклической зависимости.
Большинство циклических импортов на самом деле не являются логическими циклическими импортами, а скорее вызывают ImportError ошибки из-за способа import() вычисления инструкций верхнего уровня всего файла при вызове.
Этого ImportErrors почти всегда можно избежать, если вы хотите, чтобы ваш импорт был сверху:
Рассмотрим этот циклический импорт:
Приложение A
# profiles/serializers.py
from images.serializers import SimplifiedImageSerializer
classSimplifiedProfileSerializer(serializers.Serializer): name = serializers.CharField()
Это пытается импортировать, SimplifiedImageSerializer и если ImportError будет вызвано, поскольку оно уже импортировано, оно извлечет его из importcache.
PS: Вы должны прочитать весь этот пост голосом Дэвида Бизли.