также просто для справки кажется, что круговой импорт разрешен на python 3.5 (и, возможно, за его пределами), но не 3.4 (и, вероятно, ниже).
Чарли Паркер
4
Я использую Python 3.7.2 и все еще имею ошибку времени выполнения из-за циклических зависимостей.
Ричард Уайтхед
Ответы:
282
В прошлом году на comp.lang.python было действительно хорошее обсуждение этого вопроса . Он довольно подробно отвечает на ваш вопрос.
Импорт довольно прост на самом деле. Просто запомните следующее:
'import' и 'from xxx import yyy' являются исполняемыми операторами. Они выполняются, когда запущенная программа достигает этой строки.
Если модуль отсутствует в sys.modules, то при импорте создается новая запись модуля в sys.modules, а затем выполняется код в модуле. Он не возвращает управление вызывающему модулю, пока выполнение не завершится.
Если модуль существует в sys.modules, то импорт просто возвращает этот модуль независимо от того, завершил ли он выполнение. По этой причине циклический импорт может возвращать модули, которые кажутся частично пустыми.
Наконец, исполняемый скрипт выполняется в модуле с именем __main__, импорт скрипта под его собственным именем создаст новый модуль, не связанный с __main__.
Возьмите это вместе, и вы не должны удивляться при импорте модулей.
@meawoppl Не могли бы вы расширить этот комментарий, пожалуйста? Как конкретно они изменились?
Дэн Шиен
3
На данный момент единственная ссылка на циклический импорт в python3 "Что нового?" страниц в 3.5 . В нем говорится «Круговой импорт, связанный с относительным импортом, теперь поддерживается». @meawoppl Вы нашли что-нибудь еще, что не указано на этих страницах?
Зезолло
4
Они деф. не поддерживается в 3.0-3.4. Или, по крайней мере, семантика успеха различна. Вот краткий обзор, в котором я не упомянул 3,5 изменения. gist.github.com/datagrok/40bf84d5870c41a77dc6
meawoppl
Не могли бы вы рассказать подробнее об этом: «Наконец, исполняемый скрипт запускается в модуле с именем main , при импорте скрипта под собственным именем будет создан новый модуль, не связанный с main ». Итак, допустим, что файл является a.py, и когда он запускается как основная точка входа, теперь он является основным, если в нем есть код, подобный импорту некоторой переменной. Тогда будет ли тот же файл 'a.py' загружаться в таблицу модулей sys? Так значит ли это, что если он имеет оператор print, то он будет выполняться дважды? Один раз для основного файла и еще раз, когда импорт встречается?
переменная
Этому ответу 10 лет, и я хотел бы модернизированное обновление, чтобы гарантировать, что оно остается правильным в различных версиях Python, 2.x или 3.x
Fallenreaper
296
Если вы делаете import fooвнутри barи import barвнутри foo, это будет работать нормально. К тому времени, когда что-то действительно запустится, оба модуля будут полностью загружены и будут иметь ссылки друг на друга.
Проблема в том, когда вместо этого вы делаете from foo import abcи from bar import xyz. Потому что теперь каждый модуль требует, чтобы другой модуль уже был импортирован (чтобы существующее имя импортировалось), прежде чем его можно будет импортировать.
Кажется, что from foo import *и from bar import *будет работать нормально.
Акавалл,
1
Проверьте редактирование поста выше, используя a.py/b.py. Он не использует from x import y, и все же получает круговую ошибку импорта
Грег Эннис
2
Это не совсем правда. Точно так же, как import * from, если вы попытаетесь получить доступ к элементу циклического импорта на верхнем уровне, то есть до того, как скрипт завершит свой запуск, у вас возникнет та же проблема. Например, если вы устанавливаете глобальный пакет в одном пакете из другого, и они оба включают друг друга. Я делал это, чтобы создать неаккуратную фабрику для объекта в базовом классе, где этот объект мог бы быть одним из множества подклассов, и при использовании кода не нужно было знать, что он фактически создает.
AaronM
3
@Akavall Не совсем. Это будет импортировать только имена, которые доступны при выполнении importинструкции. Таким образом, он не выдаст ошибку, но вы можете не получить все ожидаемые переменные.
Авгурар
3
Обратите внимание, если вы делаете from foo import *и from bar import *, все выполнено в fooнаходится в фазе инициализации bar, а фактические функции до barсих пор не определена ...
Martian2049
100
Циклический импорт завершается, но вы должны быть осторожны, чтобы не использовать циклически импортированные модули во время инициализации модуля.
Рассмотрим следующие файлы:
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.
При попытке доступа 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.
это сильно объясняет проблему, но как насчет решения? как мы могли бы правильно импортировать и распечатать x? другое решение выше не сработало для меня
mehmet
Я думаю, что этот ответ принесет большую пользу, если вы используете __name__вместо 'a'. Вначале я был совершенно сбит с толку, почему файл будет выполнен дважды.
Берги
30
Как и другие ответы описывают этот шаблон приемлем в Python:
def dostuff(self):from foo import bar
...
Что позволит избежать выполнения оператора импорта, когда файл импортируется другими модулями. Только если есть логическая циклическая зависимость, это потерпит неудачу.
Большинство циклических импортов на самом деле не являются логическими циклическими импортами, а скорее вызывают ImportErrorошибки из-за способа import()оценки операторов верхнего уровня всего файла при вызове.
Этого ImportErrorsпочти всегда можно избежать, если вы хотите, чтобы ваш импорт был на вершине :
Рассмотрим этот круговой импорт:
Приложение А
# profiles/serializers.pyfrom images.serializers importSimplifiedImageSerializerclassSimplifiedProfileSerializer(serializers.Serializer):
name = serializers.CharField()classProfileSerializer(SimplifiedProfileSerializer):
recent_images =SimplifiedImageSerializer(many=True)
Приложение B
# images/serializers.pyfrom profiles.serializers importSimplifiedProfileSerializerclassSimplifiedImageSerializer(serializers.Serializer):
title = serializers.CharField()classImageSerializer(SimplifiedImageSerializer):
profile =SimplifiedProfileSerializer()
Ошибка ImportError не вызывается, если модуль уже был импортирован. Модули можно импортировать столько раз, сколько вы хотите, т.е. «import a; import a;» в порядке.
Юрас
9
У меня есть пример, который поразил меня!
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
Как вы это исправили? Я пытаюсь понять циклический импорт, чтобы решить мою собственную проблему, которая очень похожа на то, что вы делаете ...
c089
12
Хм ... я думаю, что решил эту проблему с этим невероятно уродливым хаком. {{{если не 'foo.bar' в sys.modules: из панели импорта foo else: bar = sys.modules ['foo.bar']}}} Лично я считаю, что циклический импорт - ОГРОМНЫЙ предупреждающий знак о плохом коде дизайн ...
c089
5
@ c089, или вы могли бы просто двигаться import barв foo.pyдо конца
warvariuc
5
Если barи то, и fooдругое нужно использовать gX, самое «чистое» решение - это вставить gXдругой модуль, иметь оба fooи barимпортировать этот модуль. (самый чистый в том смысле, что нет никаких скрытых семантических зависимостей.)
Тим Уайлдер
2
Тим имеет хорошую точку зрения. В основном это потому, что barне могу найти gXв foo. круговой импорт хорош сам по себе, но он gXне определен при импорте.
Martian2049
9
Модуль a.py:
import b
print("This is from module a")
Модуль б.пи
import a
print("This is from module b")
Запуск «Модуль А» будет выводить:
>>>'This is from module a''This is from module b''This is from module a'>>>
Он вывел эти 3 строки, в то время как должен был вывести бесконечность из-за циклического импорта. То, что происходит построчно при запуске «Модуля А», указано здесь:
Первая линия import b. поэтому он посетит модуль B
Первая строка в модуле b import a. поэтому он посетит модуль А
Первая строка в модуле a, import bно обратите внимание, что эта строка больше не будет выполняться , потому что каждый файл в python выполняет строку импорта только один раз, не имеет значения, где и когда она выполняется. поэтому он перейдет к следующей строке и напечатает "This is from module a".
После завершения посещения всего модуля a из модуля b мы все еще находимся в модуле b. поэтому следующая строка напечатает"This is from module b"
Строки модуля b выполнены полностью. поэтому мы вернемся к модулю a, где мы начали модуль b.
Строка import b уже выполнена и больше не будет выполняться. Следующая строка будет напечатана "This is from module a"и программа будет завершена.
Я полностью согласен с ответом питона здесь. Но я наткнулся на некоторый код, который имел недостатки при круговом импорте и вызывал проблемы при попытке добавить модульные тесты. Таким образом, чтобы быстро исправить это без изменения всего, вы можете решить проблему, выполнив динамический импорт.
# Hack to import something without circular import issuedef load_module(name):"""Load module using imp.find_module"""
names = name.split(".")
path =Nonefor 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
from b import B
class A:@staticmethoddef save_result(result):print('save the result')@staticmethoddef do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))@staticmethoddef do_something_related_to_b(param):
B.do_something_b_ish(param)
Файл b.py
from a import A
class B:@staticmethoddef do_something_b_ish(param):
A.save_result(B.use_param_like_b_would(param))
В этом случае просто переместите один статический метод в отдельный файл, скажем c.py:
Файл c.py
def save_result(result):print('save the result')
позволит удалить save_resultметод из A, и, следовательно, позволит удалить импорт A из a в b:
Рефакторированный файл a.py
from b import B
from c import save_result
class A:@staticmethoddef do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))@staticmethoddef do_something_related_to_b(param):
B.do_something_b_ish(param)
Восстановленный файл b.py
from c import save_result
class B:@staticmethoddef do_something_b_ish(param):
save_result(B.use_param_like_b_would(param))
Таким образом, если у вас есть инструмент (например, pylint или PyCharm), который сообщает о методах, которые могут быть статическими, простое наложение staticmethodдекоратора на них может быть не лучшим способом заставить замолчать предупреждение. Несмотря на то, что метод, похоже, связан с классом, может быть, лучше выделить его, особенно если у вас есть несколько тесно связанных модулей, которым может потребоваться такая же функциональность, и вы намерены применять принципы DRY.
Круговой импорт может сбивать с толку, потому что импорт делает две вещи:
выполняет импортированный код модуля
добавляет импортированный модуль в глобальную таблицу символов модуля
Первое выполняется только один раз, а второе - в каждом операторе импорта. Круговой импорт создает ситуацию, когда импортирующий модуль использует импортированный с частично выполненным кодом. В результате он не увидит объекты, созданные после оператора импорта. Ниже пример кода демонстрирует это.
Круговой импорт не является последним злом, которого следует избегать любой ценой. В некоторых средах, таких как 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 =3print'b imports a'import a
y =5print"b out"
a.py
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, и, таким образом, вы исключаете любой циклический импорт.
Например:
Файл:
class A(object):def __init__(self, name):
self.name = name
CLASS = A("me")import b
go = B(6)
go.dostuff
Файл б:
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."
from a import CLASSна самом деле не пропускает выполнение всего кода в a.py. Вот что действительно происходит: (1) Весь код в a.py запускается как специальный модуль «__main__». (2) При import bзапуске запускается код верхнего уровня в b.py (определяющий класс B), а затем управление возвращается к «__main__». (3) "__main__" в конечном итоге передает управление go.dostuff(). (4) когда приходит dostuff () import a, он снова запускает весь код в a.py , на этот раз как модуль «a»; затем он импортирует объект CLASS из нового модуля "a". Так что на самом деле, это будет работать одинаково хорошо, если вы используете import aгде-нибудь в b.py.
Ответы:
В прошлом году на comp.lang.python было действительно хорошее обсуждение этого вопроса . Он довольно подробно отвечает на ваш вопрос.
источник
Если вы делаете
import foo
внутриbar
иimport bar
внутриfoo
, это будет работать нормально. К тому времени, когда что-то действительно запустится, оба модуля будут полностью загружены и будут иметь ссылки друг на друга.Проблема в том, когда вместо этого вы делаете
from foo import abc
иfrom bar import xyz
. Потому что теперь каждый модуль требует, чтобы другой модуль уже был импортирован (чтобы существующее имя импортировалось), прежде чем его можно будет импортировать.источник
from foo import *
иfrom bar import *
будет работать нормально.from x import y
, и все же получает круговую ошибку импортаimport
инструкции. Таким образом, он не выдаст ошибку, но вы можете не получить все ожидаемые переменные.from foo import *
иfrom bar import *
, все выполнено вfoo
находится в фазе инициализацииbar
, а фактические функции доbar
сих пор не определена ...Циклический импорт завершается, но вы должны быть осторожны, чтобы не использовать циклически импортированные модули во время инициализации модуля.
Рассмотрим следующие файлы:
a.py:
b.py:
Если вы выполните a.py, вы получите следующее:
При втором импорте b.py (во втором
a in
) интерпретатор Python больше не импортируетb
, поскольку он уже существует в модуле dict.При попытке доступа
b.x
отa
во время инициализации модуля, вы получаетеAttributeError
.Добавьте следующую строку к
a.py
:Затем вывод:
Это связано с тем, что модули выполняются при импорте, и на момент
b.x
обращения к строкеx = 3
еще не выполнено, что произойдет только послеb out
.источник
__name__
вместо'a'
. Вначале я был совершенно сбит с толку, почему файл будет выполнен дважды.Как и другие ответы описывают этот шаблон приемлем в Python:
Что позволит избежать выполнения оператора импорта, когда файл импортируется другими модулями. Только если есть логическая циклическая зависимость, это потерпит неудачу.
Большинство циклических импортов на самом деле не являются логическими циклическими импортами, а скорее вызывают
ImportError
ошибки из-за способаimport()
оценки операторов верхнего уровня всего файла при вызове.Этого
ImportErrors
почти всегда можно избежать, если вы хотите, чтобы ваш импорт был на вершине :Рассмотрим этот круговой импорт:
Приложение А
Приложение B
От Дэвида Бизли отличные разговорные модули и пакеты: живи и дай умереть! - PyCon 2015 ,
1:54:00
вот способ борьбы с циклическим импортом в python:Он пытается импортировать,
SimplifiedImageSerializer
и еслиImportError
он вызван, потому что он уже импортирован, он извлечет его из кэша импорта.PS: Вы должны прочитать весь этот пост в голосе Дэвида Бизли.
источник
У меня есть пример, который поразил меня!
foo.py
bar.py
main.py
В командной строке: $ python main.py
источник
import bar
вfoo.py
до концаbar
и то, иfoo
другое нужно использоватьgX
, самое «чистое» решение - это вставитьgX
другой модуль, иметь обаfoo
иbar
импортировать этот модуль. (самый чистый в том смысле, что нет никаких скрытых семантических зависимостей.)bar
не могу найтиgX
в foo. круговой импорт хорош сам по себе, но онgX
не определен при импорте.Модуль a.py:
Модуль б.пи
Запуск «Модуль А» будет выводить:
Он вывел эти 3 строки, в то время как должен был вывести бесконечность из-за циклического импорта. То, что происходит построчно при запуске «Модуля А», указано здесь:
import b
. поэтому он посетит модуль Bimport a
. поэтому он посетит модуль Аimport b
но обратите внимание, что эта строка больше не будет выполняться , потому что каждый файл в python выполняет строку импорта только один раз, не имеет значения, где и когда она выполняется. поэтому он перейдет к следующей строке и напечатает"This is from module a"
."This is from module b"
"This is from module a"
и программа будет завершена.источник
Я полностью согласен с ответом питона здесь. Но я наткнулся на некоторый код, который имел недостатки при круговом импорте и вызывал проблемы при попытке добавить модульные тесты. Таким образом, чтобы быстро исправить это без изменения всего, вы можете решить проблему, выполнив динамический импорт.
Опять же, это не постоянное исправление, но может помочь кому-то, кто хочет исправить ошибку импорта, не изменяя слишком много кода.
Ура!
источник
Здесь много хороших ответов. Хотя обычно есть быстрые решения проблемы, некоторые из которых кажутся более питонными, чем другие, если вы можете позволить себе некоторую рефакторинг, другой подход - проанализировать организацию вашего кода и попытаться удалить циклическую зависимость. Например, вы можете обнаружить, что у вас есть:
Файл a.py
Файл b.py
В этом случае просто переместите один статический метод в отдельный файл, скажем
c.py
:Файл c.py
позволит удалить
save_result
метод из A, и, следовательно, позволит удалить импорт A из a в b:Рефакторированный файл a.py
Восстановленный файл b.py
Таким образом, если у вас есть инструмент (например, pylint или PyCharm), который сообщает о методах, которые могут быть статическими, простое наложение
staticmethod
декоратора на них может быть не лучшим способом заставить замолчать предупреждение. Несмотря на то, что метод, похоже, связан с классом, может быть, лучше выделить его, особенно если у вас есть несколько тесно связанных модулей, которым может потребоваться такая же функциональность, и вы намерены применять принципы DRY.источник
Круговой импорт может сбивать с толку, потому что импорт делает две вещи:
Первое выполняется только один раз, а второе - в каждом операторе импорта. Круговой импорт создает ситуацию, когда импортирующий модуль использует импортированный с частично выполненным кодом. В результате он не увидит объекты, созданные после оператора импорта. Ниже пример кода демонстрирует это.
Круговой импорт не является последним злом, которого следует избегать любой ценой. В некоторых средах, таких как Flask, они вполне естественны, и настройка вашего кода для их устранения не делает код лучше.
main.py
b.by
a.py
вывод python main.py с комментариями
источник
Я решил проблему следующим образом, и она работает без ошибок. Рассмотрим два файла
a.py
иb.py
.Я добавил это,
a.py
и это сработало.a.py:
b.py:
Я получаю вывод
источник
Хорошо, я думаю, у меня есть довольно крутое решение. Допустим, у вас есть файл
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
, и, таким образом, вы исключаете любой циклический импорт.Например:
Файл:
Файл б:
Вуаля.
источник
from a import CLASS
на самом деле не пропускает выполнение всего кода в a.py. Вот что действительно происходит: (1) Весь код в a.py запускается как специальный модуль «__main__». (2) Приimport b
запуске запускается код верхнего уровня в b.py (определяющий класс B), а затем управление возвращается к «__main__». (3) "__main__" в конечном итоге передает управлениеgo.dostuff()
. (4) когда приходит dostuff ()import a
, он снова запускает весь код в a.py , на этот раз как модуль «a»; затем он импортирует объект CLASS из нового модуля "a". Так что на самом деле, это будет работать одинаково хорошо, если вы используетеimport a
где-нибудь в b.py.