Циркулярный (или циклический) импорт в Python

Что произойдет, если два модуля импортируют друг друга?

Чтобы обобщить проблему, как насчет циклического импорта в Python?

7 Solutions collect form web for “Циркулярный (или циклический) импорт в Python”

Об этом было очень хорошо обсуждено на comp.lang.python в прошлом году. Он очень хорошо отвечает на ваш вопрос.

Импорт довольно прямолинейный. Просто помните следующее:

«import» и «from xxx import yyy» – исполняемые операторы. Они выполняются, когда запущенная программа достигает этой строки.

Если модуль отсутствует в sys.modules, тогда импорт создает новую запись модуля в sys.modules и затем выполняет код в модуле. Он не возвращает управление вызывающему модулю до завершения выполнения.

Если модуль существует в sys.modules, тогда импорт просто возвращает этот модуль независимо от того, завершил ли он выполнение. Вот почему циклический импорт может возвращать модули, которые кажутся частично пустыми.

Наконец, исполняемый скрипт запускается в модуле с именем __main__, при импорте скрипта под его собственным именем будет создан новый модуль, не связанный с __main__.

Возьмите эту партию вместе, и вы не должны получать никаких сюрпризов при импорте модулей.

Если вы import foo внутреннюю bar import foo и bar import bar внутри foo , она будет работать нормально. К тому моменту, когда что-то действительно выполняется, оба модуля будут полностью загружены и будут иметь ссылки друг на друга.

Проблема в том, что вместо этого вы делаете from foo import abc и from bar import xyz . Потому что теперь каждый модуль требует, чтобы другой модуль уже был импортирован (так что имя, которое мы импортируем), прежде чем оно может быть импортировано.

Циклический импорт заканчивается, но вам нужно быть осторожным, чтобы не использовать циклически импортированные модули во время инициализации модуля.

Рассмотрим следующие файлы:

a.py:

 print "a in" import sys print "b imported: %s" % ("b" in sys.modules, ) import b print "a out" 

b.py:

 print "b in" import a print "b out" x = 3 

Если вы выполните a.py, вы получите следующее:

 $ 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.

Если вы попытаетесь получить доступ к bx из инициализации модуля, вы получите AttributeError .

Добавьте в a.py следующую строку:

 print bx 

Затем вывод:

 $ 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 bx AttributeError: 'module' object has no attribute 'x' 

Это связано с тем, что модули выполняются при импорте и в момент доступа к bx линия x = 3 еще не выполнена, что произойдет только после b out .

Как и другие ответы, эта модель приемлема для python:

 def dostuff(self): from foo import bar ... 

Это позволит избежать выполнения оператора импорта, когда файл импортируется другими модулями. Только если существует логическая циклическая зависимость, это не удастся.

Большинство циркулярных импортов на самом деле не являются логическим циклическим импортом, а скорее ImportError ошибки ImportError из-за того, как import() оценивает операторы верхнего уровня всего файла при вызове.

Эти ImportErrors можно почти всегда избегать, если вы хотите, чтобы ваш импорт был сверху :

Рассмотрим этот круговой импорт:

Приложение А

 # profiles/serializers.py from images.serializers import SimplifiedImageSerializer class SimplifiedProfileSerializer(serializers.Serializer): name = serializers.CharField() class ProfileSerializer(SimplifiedProfileSerializer): recent_images = SimplifiedImageSerializer(many=True) 

Приложение B

 # images/serializers.py from profiles.serializers import SimplifiedProfileSerializer class SimplifiedImageSerializer(serializers.Serializer): title = serializers.CharField() class ImageSerializer(SimplifiedImageSerializer): profile = SimplifiedProfileSerializer() 

От David Beazleys отличные разговоры Модули и пакеты: Live и Let Die! – PyCon 2015 , 1:54:00 , вот способ справиться с циклическим импортом в python:

 try: from images.serializers import SimplifiedImageSerializer except ImportError: import sys SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer'] 

Это пытается импортировать SimplifiedImageSerializer и если ImportError поднят, потому что он уже импортирован, он вытащит его из importcache.

PS: Вы должны прочитать весь пост в голосе Дэвида Бэйсли.

У меня есть пример, который поразил меня!

foo.py

 import bar class gX(object): g = 10 

bar.py

 from foo import gX o = gX() 

main.py

 import foo import bar print "all done" 

В командной строке: $ python main.py

 Traceback (most recent call last): File "m.py", line 1, in <module> import foo File "/home/xolve/foo.py", line 1, in <module> import bar File "/home/xolve/bar.py", line 1, in <module> from foo import gX ImportError: cannot import name gX 

Я полностью согласен с ответом pythoneer здесь. Но я наткнулся на какой-то код, который был испорчен циркулярным импортом и вызвал проблемы при попытке добавить модульные тесты. Поэтому, чтобы быстро исправить это, не изменяя все, вы можете решить проблему, выполнив динамический импорт.

 # Hack to import something without circular import issue def load_module(name): """Load module using imp.find_module""" names = name.split(".") path = None for name in names: f, path, info = imp.find_module(name, path) path = [path] return imp.load_module(name, f, path[0], info) constants = load_module("app.constants") 

Опять же, это не постоянное исправление, но может помочь кому-то, кто хочет исправить ошибку импорта, не изменяя слишком много кода.

Ура!

Хорошо, я думаю, что у меня довольно крутое решение. Допустим, у вас есть файл a и файл b . У вас есть def или class в файле b который вы хотите использовать в модуле a , но у вас есть что-то еще, либо def , class , либо переменная из файла a который вам нужен в вашем определении или классе в файле b . Что вы можете сделать, в нижней части файла a , после вызова функции или класса в файле a которая необходима в файле b , но прежде чем вызывать функцию или класс из файла b который вам нужен для файла a , скажем, import b Then , и вот ключевая часть , во всех определениях или классах в файле b которые нуждаются в def или class из файла a (назовем его CLASS ), вы скажете from a import CLASS

Это работает, потому что вы можете импортировать файл b без Python, выполняющего какие-либо из операторов импорта в файле b , и, таким образом, вы избегаете любых циклических импорта.

Например:

Файл a:

 class A(object): def __init__(self, name): self.name = name CLASS = A("me") import b go = B(6) go.dostuff 

Файл b:

 class B(object): def __init__(self, number): self.number = number def dostuff(self): from a import CLASS print "Hello " + CLASS.name + ", " + str(number) + " is an interesting number." 

Вуаля.

Interesting Posts

Точное совпадение строк

Локальный сервер дает неправильные файлы. Возможно ли, что я запускаю 2 сервера python?

Анализ настроений по большому набору текста интерактивной беседы

Карта всех пунктов ниже определенного времени поездки?

Не удалось загрузить пакеты в PyPI: 410 Gone

Почему конечное повторение в lookbehind не работает в некоторых вариантах?

Unittest setUp / tearDown для нескольких тестов

Как я могу более легко подавить предыдущие исключения, когда я выражу свое собственное исключение в ответ?

BeautifulSoup: Как извлечь все <li> s из списка <ul> s, который содержит некоторые вложенные <ul> s?

Ubuntu с запуском `pip install` дает ошибку 'Невозможно построить следующие необходимые пакеты: * freetype'

Пиктограммы python pexpect sendcontrol

Как я представляю и работаю с n-битовыми векторами в Python?

networkx – изменить цвет / ширину в соответствии с атрибутами края – непоследовательный результат

Увеличение пропускной способности в скрипте python

Извлечение ценности в Beautifulsoup

Python - лучший язык программирования в мире.