Круговой импорт Python?
Так что я получаю эту ошибку
Traceback (most recent call last):
File "/Users/alex/dev/runswift/utils/sim2014/simulator.py", line 3, in <module>
from world import World
File "/Users/alex/dev/runswift/utils/sim2014/world.py", line 2, in <module>
from entities.field import Field
File "/Users/alex/dev/runswift/utils/sim2014/entities/field.py", line 2, in <module>
from entities.goal import Goal
File "/Users/alex/dev/runswift/utils/sim2014/entities/goal.py", line 2, in <module>
from entities.post import Post
File "/Users/alex/dev/runswift/utils/sim2014/entities/post.py", line 4, in <module>
from physics import PostBody
File "/Users/alex/dev/runswift/utils/sim2014/physics.py", line 21, in <module>
from entities.post import Post
ImportError: cannot import name Post
и вы можете видеть, что я использую тот же оператор импорта дальше, и он работает? Есть ли какое-то неписаное правило о круговом импорте? Как использовать один и тот же класс дальше по стеку вызовов?
5 ответов:
Я думаю, что в настоящее время принятый ответ jpmc26 слишком сильно зависит от кругового импорта. Они могут работать просто отлично, если вы установите их правильно.
самый простой способ сделать это с помощью
import my_module
синтаксис, а неfrom my_module import some_object
. Первый почти всегда будет работать, даже еслиmy_module
включено импортирует нас обратно. Последний работает только еслиmy_object
уже определен вmy_module
, что в циклическом импорте может быть не так.в вашем случае: попробуйте изменение
entities/post.py
сделатьimport physics
наphysics.PostBody
а не простоPostBody
напрямую. Аналогично, изменитьphysics.py
сделатьimport post
и затем использоватьpost.Post
а не простоPost
.
при первом импорте модуля (или его элемента) код внутри модуля выполняется последовательно, как и любой другой код; например, он не обрабатывается иначе, чем тело функции. Ан
import
- Это просто команда, как и любой другой (присваивания, вызов функцииdef
,class
). Предполагая, что ваш импорт происходит в верхней части скрипта, то вот что происходит:
- при попытке импорта
World
Сworld
наworld
скрипт получает выполненный.- The
world
импорт скриптаField
, который причиняетentities.field
скрипт для выполнения.- этот процесс продолжается до тех пор, пока вы не достигнете
entities.post
скрипт, потому что вы пытались импортироватьPost
- The
entities.post
скрипт причиныphysics
модуль должен быть казнен, потому что он пытается импортироватьPostBody
- наконец,
physics
пытается импортироватьPost
Сentities.post
- я не уверен, что
entities.post
модуль существует в памяти, но это действительно не имеет значения. Либо модуль не находится в памяти, либо модуль еще не имеетPost
член, потому что не закончил выполнение для определенияPost
- в любом случае, возникает ошибка, потому что
чтобы понять циклические зависимости, вам нужно помнить, что Python по сути является языком сценариев. Выполнение операторов вне методов происходит во время компиляции. Операторы импорта выполняются так же, как вызовы методов, и чтобы понять их, вы должны думать о них как о вызовах методов.
когда вы выполняете импорт, что происходит, зависит от того, существует ли уже импортируемый файл в таблице модулей. Если это так, Python использует все, что в настоящее время находится в таблица символов. Если нет, Python начинает читать файл модуля, компилируя/выполняя / импортируя все, что он там находит. Символы, на которые ссылаются во время компиляции, найдены или нет, в зависимости от того, были ли они замечены или еще не были замечены компилятором.
представьте, что у вас есть два исходных файла:
файл X. ру
def X1: return "x1" from Y import Y2 def X2: return "x2"
файл Я. ру
def Y1: return "y1" from X import X1 def Y2: return "y2"
теперь предположим, что вы компилируете файл X. пы. Компилятор начинается с определения метода Х1, а затем попадает в оператор импорта в X. пы. Это заставляет компилятор, чтобы приостановить компиляцию Х. ру и приступить к составлению Ю. пы. Вскоре после этого компилятор делает оператор импорта в Я. ру. Поскольку Х. ру уже есть в таблице модуля, Python использует существующие неполное Х. символ пы стол, чтобы удовлетворить любой запрашиваемой ссылки. Любые символы, появляющиеся перед оператором импорта в X. ру теперь в таблице символов, но и любые символы после не. Поскольку X1 теперь появляется перед инструкцией импорта, он успешно импортированный. Python, а затем возобновляет компиляции Я. ру. При этом он определяет Y2 и заканчивается составлением Ю. пы. Затем он возобновляет подборку Х. ру, и находит У2 в Ю. символ пы стол. Компиляция в конечном итоге завершается без ошибок.
что-то совсем другое, если вы попытаетесь скомпилировать Ю. Ру из командной строки. При компиляции Я. ру, компилятор делает оператор импорта прежде, чем он определяет У2. Затем он начинает собирать Х. ру. Вскоре она попадает в оператор импорта в X. пы, которая требует Y2. Но Y2 не определен, поэтому компиляция не выполняется.обратите внимание, что если вы измените Х. ру импорт У1, компиляция всегда будет успешным, независимо от того, какой файл компиляции. Однако если вы измените файл Ю. пы импортировать символ Х2, файл не будет компилироваться.
в любое время, когда модуль X или любой модуль, импортированный X, может импортировать текущий модуль, не используйте:
from X import Y
каждый раз, когда вы думаете, что может быть циклический импорт, вы также должны избегать ссылок на время компиляции к переменным в других модулях. Рассмотрим невинно выглядящий код:
import X z = X.Y
предположим, что модуль X импортирует этот модуль до того, как этот модуль импортирует X. Далее предположим, что Y определяется в X после оператора import. Тогда Y не будет определен при импорте этого модуля, и вы получите ошибку компиляции. Если этот модуль импортирует Y первым,вы можете уйти с ним. Но когда один из ваших коллег невинно изменяет порядок определений в третьем модуле, код будет ломать.
в некоторых случаях можно разрешить циклические зависимости, переместив оператор импорта ниже определений символов, необходимых другим модулям. В приведенных выше примерах определения перед инструкцией импорта никогда не завершаются ошибкой. Определения после инструкции import иногда выходят из строя, в зависимости от порядка компиляции. Вы даже можете поместить инструкции импорта в конец файла, если ни один из импортированных символов не требуется во время компиляции.
обратите внимание, что перемещение импорт операторы вниз в модуле скрывает то, что вы делаете. Компенсируйте это с комментарием в верхней части вашего модуля что-то вроде следующего:
#import X (actual import moved down to avoid circular dependency)
в целом это плохая практика, но иногда ее трудно избежать.
для тех из вас, кто, как и я, пришел к этому вопросу из Django, вы должны знать, что документы предоставляют решение: https://docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey
" ...для ссылки на модели, определенные в другом приложении, можно явно указать модель с полной меткой приложения. Например, если модель производителя выше определена в другом приложении, называемом production, вам нужно будет использование:
class Car(models.Model): manufacturer = models.ForeignKey( 'production.Manufacturer', on_delete=models.CASCADE, )
такая ссылка может быть полезна при разрешении циклических зависимостей импорта между двумя приложениями...."