Круговой (или циклический) импорт в Python
Что произойдет, если два модуля импортируют друг друга?
чтобы обобщить проблему, как насчет циклического импорта в Python?
9 ответов
была действительно хорошая дискуссия об этом в comp.ленг.питон в прошлом году. Он отвечает на ваш вопрос довольно тщательно.
импорт довольно прост. Просто помните следующее:
'и' от ХХХ импорт ыыы являются исполняемых операторов. Они исполняют когда запущенная программа достигнет этой строки.Если модуль не находится в sys.модули, затем импорт создает новый модуль вход в системный.модули, а затем выполняет код в модуле. Это не возвращает управление вызывающему модулю до завершения выполнения.
Если модуль существует в sys.модули то импорт просто возвращает это модуль независимо от того, завершил ли он выполнение. Вот почему циклический импорт может возвращать модули, которые кажутся частично пустыми.
наконец, скрипт выполняется в модуле с именем _ _ main__, импортируя сценарий под свой собственный name создаст новый модуль, не связанный с __главный.__
возьмите эту партию вместе, и вы не должны получать никаких сюрпризов при импорте модули.
если у вас import foo
внутри bar
и import bar
внутри foo
, он будет работать нормально. К тому времени, что на самом деле работает, оба модуля будут полностью загружены и будут иметь ссылки друг на друга.
проблема в том, когда вместо этого вы делаете from foo import abc
и from bar import xyz
. Потому что теперь каждый модуль требует, чтобы другой модуль уже был импортирован (чтобы имя, которое мы импортируем, существовало), прежде чем его можно будет импортировать.
циклический импорт завершается, но вы должны быть осторожны, чтобы не использовать циклически импортированные модули во время инициализации модуля.
рассмотрим следующие файлы:
а.пы:
print "a in"
import sys
print "b imported: %s" % ("b" in sys.modules, )
import b
print "a out"
б.пы:
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
опять же, потому что он уже существует в модуль дикт.
если вы попытаетесь получить доступ к 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
.
поскольку другие ответы описывают этот шаблон, он приемлем в python:
def dostuff(self):
from foo import bar
...
что позволит избежать выполнения инструкции import при импорте файла другими модулями. Только если существует логическая циклическая зависимость, это не удастся.
наиболее круговой импорта фактически не логично круговой импорта, а поднять ImportError
ошибки, так как import()
оценивает операторы верхнего уровня всего файла, когда называемый.
эти ImportErrors
почти всегда можно избежать, если вы положительно хотите, чтобы ваш импорта на вершине:
рассмотрим этот круговой импорт:
Приложение A
# 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()
от Давида Beazleys отлично говорить модули и пакеты: живите и дайте умереть! - Команда 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.py:
import b
print("This is from module a")
модуль b.py
import a
print("This is from module b")
запуск "Module a" выведет:
>>>
'This is from module a'
'This is from module b'
'This is from module a'
>>>
он выводит эти 3 строки, в то время как он должен был выводить infinitival из-за кругового импорта. Что происходит строка за строкой при запуске "модуля a", указано здесь:
- первая строка
import b
. таким образом, он посетит модуль b - первая строка в модуле b -
import a
. так что посетим модуль a - первая строка модуля a -
import b
но обратите внимание, что эта строка больше не будет выполняться, поскольку каждый файл в python выполняет строку импорта только один раз, не имеет значения, где и когда он выполняется. так он перейдет к следующей строке и печати"This is from module a"
. - после завершения посещения всего модуля a из модуля b мы все еще находимся в модуле B. поэтому следующая строка будет печатать
"This is from module b"
- модуль линия B полностью выполнены. поэтому мы вернемся к модулю a, где мы начали модуль b.
- строка импорта b уже выполнена и не будет выполнена снова. следующая строка будет печатать
"This is from module a"
и программа будет закончена.
круговой импорт может быть запутанным, потому что импорт делает две вещи:
- он выполняет импортированный модуль код
- добавляет импортированный модуль для импорта модуля global symbol table
первое выполняется только один раз, в то время как последнее при каждом операторе импорта. Круговой импорт создает ситуацию, когда модуль импорта использует импортированный с частично выполненным кодом. В следствии он не будет видеть объекты, созданные после импорта. Ниже кода пример демонстрирует это.
круговой импорт не является окончательным злом, которого следует избегать любой ценой. В некоторых фреймворках, таких как Flask, они вполне естественны, и настройка вашего кода для их устранения не делает код лучше.
main.py
print 'import b'
import b
print 'a in globals() {}'.format('a' in globals())
print 'import a'
import a
print 'a in globals() {}'.format('a' in globals())
if __name__ == '__main__':
print 'imports done'
print 'b has y {}, a is b.a {}'.format(hasattr(b, 'y'), a is b.a)
b.by
print "b in, __name__ = {}".format(__name__)
x = 3
print 'b imports a'
import a
y = 5
print "b out"
а.ру
print 'a in, __name__ = {}'.format(__name__)
print 'a imports b'
import b
print 'b has x {}'.format(hasattr(b, 'x'))
print 'b has y {}'.format(hasattr(b, 'y'))
print "a out"
python main.py вывод с комментариями
import b
b in, __name__ = b # b code execution started
b imports a
a in, __name__ = a # a code execution started
a imports b # b code execution is already in progress
b has x True
b has y False # b defines y after a import,
a out
b out
a in globals() False # import only adds a to main global symbol table
import a
a in globals() True
imports done
b has y True, a is b.a True # all b objects are available
хорошо, я думаю, что у меня есть довольно крутое решение.
Допустим, у вас есть файл a
и файл b
.
У вас есть def
или class
в файле b
что вы хотите использовать в модуле a
, но у вас есть еще что-то, либо def
, class
, или переменная из файла a
что вам нужно в вашем определении или классе в файле b
.
То, что вы можете сделать, находится в нижней части файла a
, после вызова функции или класса в файле a
это необходимо в файле b
, но прежде чем вызов функции или класса из файла b
что нужно для файла a
, сказал import b
Тогда, и вот основная часть, во всех определениях или классы в файле 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."
вуаля.