Круговой импорт 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 67

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,
)

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

Если вы столкнетесь с этой проблемой в довольно сложном приложении, это может быть громоздким для рефакторинга всех ваших импорта. PyCharm предлагает quickfix для этого, что автоматически изменит все использование импортированных символов, а также.

enter image description here