Итак, я получаю эту ошибку
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
и вы можете видеть, что я использую тот же оператор импорта дальше, и он работает? Есть ли какое-то неписаное правило о циклическом импорте? Как мне использовать тот же класс ниже по стеку вызовов?
from
всегда работает. Если у меня естьclass A(object): pass; class C(b.B): pass
в модуле a иclass B(a.A): pass
в модуле b, то циклический импорт все еще является проблемой, и это не сработает.B
в модульa
, либо переместите классC
в модуль,b
чтобы вы могли разорвать цикл. Также стоит отметить, что даже если только в одном направлении круга задействован код верхнего уровня (например, если классC
не существует), вы можете получить ошибку, в зависимости от того, какой модуль был импортирован первым другим кодом.from . import sibling_module
, нетfrom .sibling_module import SomeClass
). Есть еще одна тонкость, когда__init__.py
файл пакета участвует в циклическом импорте, но проблема возникает редко и, вероятно, является ошибкой вimport
реализации. См. Ошибку Python 23447 , для которой я отправил патч (который, к сожалению , не работает).Когда вы импортируете модуль (или его член) в первый раз, код внутри модуля выполняется последовательно, как и любой другой код; например, он не обрабатывается иначе, чем тело функции.
import
Просто команда , как и любого другого (присвоение, вызов функции,def
,class
). Предполагая, что ваш импорт происходит в верхней части скрипта, происходит следующее:World
изworld
,world
скрипт запускается.world
импорте сценарияField
, который вызываетentities.field
сценарий , чтобы получить казнена.entities.post
скрипта, потому что вы пытались импортироватьPost
entities.post
Скрипт вызываетphysics
модуль будет выполняться , поскольку он пытается импортPostBody
physics
пытается импортироватьPost
изentities.post
entities.post
существует ли модуль в памяти, но это не имеет значения. Либо модуля нет в памяти, либо у модуля еще нетPost
члена, потому что он не завершил выполнение для определенияPost
Post
нет файла для импортаТак что нет, он не «работает дальше в стеке вызовов». Это трассировка стека того, где произошла ошибка, что означает, что при импорте
Post
в этот класс произошла ошибка . Вам не следует использовать циклический импорт. В лучшем случае это дает незначительную пользу (как правило, никакой пользы) и вызывает подобные проблемы. Это обременяет любого разработчика, обслуживающего его, заставляя ходить по яичной скорлупе, чтобы не сломать ее. Реорганизуйте организацию модуля.источник
isinstance(userData, Post)
. В любом случае у вас нет выбора. Циклический импорт работать не будет. Тот факт, что у вас есть циклический импорт, для меня является запахом кода. Он предполагает, что у вас есть некоторые функции, которые следует перенести в третий модуль. Я не мог сказать что, не глядя на оба класса целиком.def
не выполняется до тех пор, пока функция не будет вызвана, поэтому импорт не произойдет, пока вы не вызовете функцию. К тому времениimport
s должен работать, поскольку один из модулей будет полностью импортирован до вызова. Это абсолютно отвратительный прием, и он не должен оставаться в вашей кодовой базе в течение длительного времени.import foo
а неfrom foo import Bar
. Это потому, что большинство модулей просто определяют вещи (например, функции и классы), которые запускаются позже. Модули, которые делают важные вещи при их импорте (например, скрипт, не защищенныйif __name__ == "__main__"
), могут по-прежнему вызывать проблемы, но это не так часто.Чтобы понять циклические зависимости, вам нужно помнить, что Python - это, по сути, язык сценариев. Выполнение операторов вне методов происходит во время компиляции. Операторы импорта выполняются так же, как вызовы методов, и чтобы понять их, вы должны думать о них как о вызовах методов.
Что происходит при импорте, зависит от того, существует ли уже импортируемый файл в таблице модуля. Если это так, Python использует все, что в данный момент находится в таблице символов. Если нет, Python начинает читать файл модуля, компилируя / выполняя / импортируя все, что он там находит. Символы, на которые ссылаются во время компиляции, обнаруживаются или нет, в зависимости от того, были ли они видны компилятору или еще не увидены компилятором.
Представьте, что у вас есть два исходных файла:
Файл X.py
Файл Y.py
Теперь предположим, что вы компилируете файл X.py. Компилятор начинает с определения метода X1, а затем выполняет оператор импорта в X.py. Это заставляет компилятор приостановить компиляцию X.py и начать компиляцию Y.py. Вскоре после этого компилятор выполняет оператор импорта в Y.py. Поскольку X.py уже находится в таблице модулей, Python использует существующую неполную таблицу символов X.py для удовлетворения любых запрошенных ссылок. Любые символы, появляющиеся перед оператором импорта в X.py, теперь находятся в таблице символов, а символы после них - нет. Поскольку X1 теперь появляется перед оператором импорта, он успешно импортирован. Затем Python возобновляет компиляцию Y.py. При этом он определяет Y2 и завершает компиляцию Y.py. Затем он возобновляет компиляцию X.py и находит Y2 в таблице символов Y.py. В конечном итоге компиляция завершается без ошибки.
Что-то совсем другое произойдет, если вы попытаетесь скомпилировать Y.py из командной строки. При компиляции Y.py компилятор выполняет оператор импорта до того, как он определит Y2. Затем начинается компиляция X.py. Вскоре он попадает в оператор импорта в X.py, который требует Y2. Но Y2 не определен, поэтому компиляция не выполняется.
Обратите внимание, что если вы измените X.py для импорта Y1, компиляция всегда будет успешной, независимо от того, какой файл вы компилируете. Однако если вы измените файл Y.py для импорта символа X2, ни один файл не будет компилироваться.
Каждый раз, когда модуль X или любой модуль, импортированный X, может импортировать текущий модуль, НЕ используйте:
Каждый раз, когда вы думаете, что может иметь место циклический импорт, вам также следует избегать ссылок на переменные во время компиляции в других модулях. Рассмотрим невинно выглядящий код:
Предположим, что модуль X импортирует этот модуль до того, как этот модуль импортирует X. Далее предположим, что Y определен в X после оператора импорта. Тогда Y не будет определяться при импорте этого модуля, и вы получите ошибку компиляции. Если этот модуль сначала импортирует Y, вы можете обойтись без него. Но когда один из ваших коллег невинно меняет порядок определений в третьем модуле, код ломается.
В некоторых случаях вы можете разрешить циклические зависимости, переместив оператор импорта вниз под определениями символов, необходимых для других модулей. В приведенных выше примерах определения перед оператором импорта никогда не ошибаются. Определения после оператора импорта иногда терпят неудачу, в зависимости от порядка компиляции. Вы даже можете поместить операторы импорта в конец файла, если ни один из импортированных символов не нужен во время компиляции.
Обратите внимание, что перемещение операторов импорта вниз в модуле скрывает то, что вы делаете. Компенсируйте это с помощью комментария в верхней части модуля примерно следующего содержания:
В целом это плохая практика, но иногда ее трудно избежать.
источник
Тем из вас, кто, как и я, пришел к этой проблеме из Django, вы должны знать, что в документации есть решение: https://docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey
"... Чтобы ссылаться на модели, определенные в другом приложении, вы можете явно указать модель с полной меткой приложения. Например, если указанная выше модель производителя определена в другом приложении под названием production, вам нужно будет использовать:
Справочник такого типа может быть полезен при разрешении зависимостей циклического импорта между двумя приложениями. ... "
источник
Мне удалось импортировать модуль внутри функции (только), для которой требуются объекты из этого модуля:
источник
Если вы столкнетесь с этой проблемой в довольно сложном приложении, рефакторинг всего вашего импорта может оказаться обременительным. PyCharm предлагает быстрое исправление для этого, которое также автоматически изменит все использование импортированных символов.
источник
Я использовал следующее:
но чтобы избавиться от этого,
circular reference
я сделал следующее, и это сработало:источник