Как решить проблему "ImportError: Невозможно импортировать имя X" или "AttributeError: ... (вероятно, из-за циклического импорта)"?
У меня есть код, распределенный по нескольким файлам, которые пытаются импортировать друг друга следующим образом:
main.py:
from entity import Ent
entity.py:
from physics import Physics
class Ent:
...
physics.py:
from entity import Ent
class Physics:
...
Когда я запускаю main.py, я получаю следующую ошибку:
Traceback (most recent call last):
File "main.py", line 2, in <module>
from entity import Ent
File ".../entity.py", line 5, in <module>
from physics import Physics
File ".../physics.py", line 2, in <module>
from entity import Ent
ImportError: cannot import name Ent
Я предполагаю, что ошибка связана с тем, что я дважды импортирую entity — один раз в main.py, а затем снова в physics.py. Как я могу обойти эту проблему?
5 ответ(ов)
У вас возникают проблемы с круговыми зависимыми импортами. Модуль physics.py
импортируется из модуля entity
до того, как класс Ent
будет определен, а в модуле physics
происходит попытка импортировать entity
, который уже находится в процессе инициализации. Чтобы решить эту проблему, необходимо удалить зависимость от модуля physics
в модуле entity
.
Хотя вы определенно должны избегать циклических зависимостей, вы можете отложить импорт в Python.
Например:
import SomeModule
def someFunction(arg):
from some.dependency import DependentClass
Это (по крайней мере в некоторых случаях) поможет избежать ошибки.
Отложенный импорт позволяет избежать проблемы с последовательностью загрузки модулей, так как зависимость будет загружена только в момент вызова функции, тем самым уменьшая вероятность возникновения циклических зависимостей. Однако лучше всего стараться проектировать модули так, чтобы избежать таких ситуаций вовсе.
Это круговая зависимость, которую можно решить без каких-либо структурных изменений в коде. Проблема возникает из-за того, что в vector
вы требуете, чтобы entity
был доступен для использования сразу, и наоборот. Причина этой проблемы заключается в том, что вы пытаетесь получить доступ к содержимому модуля до его готовности, используя конструкцию from x import y
. На самом деле это почти то же самое, что и:
import x
y = x.y
del x
Python способен обнаруживать круговые зависимости и предотвращать бесконечные циклы импорта. В основном все, что происходит, это создание пустого заполнителя для модуля (то есть он не содержит содержимого). Как только модули с круговой зависимостью компилируются, он обновляет импортированный модуль. Это работает примерно так:
a = module() # импортируем a
# остальная часть модуля
a.update_contents(real_a)
Для того чтобы Python мог работать с круговыми зависимостями, вы должны использовать стиль import x
:
import x
class cls:
def __init__(self):
self.y = x.y
Поскольку вы больше не ссылаетесь на содержимое модуля на верхнем уровне, Python может скомпилировать модуль, не обращая внимания на содержимое круговой зависимости. Под верхним уровнем я имею в виду строки, которые будут выполняться во время компиляции, в отличие от содержимого функций (например, y = x.y
). Статические или классные переменные, обращающиеся к содержимому модуля, также будут вызывать проблемы.
В моем случае я работал в Jupyter Notebook, и эта проблема возникла из-за того, что импорт уже был кэширован, так как я определял класс/функцию внутри своего рабочего файла.
Я перезапустил ядро Jupyter, и ошибка исчезла.
Чтобы сделать логику понятной, важно понимать, что проблема возникает из-за бесконечного цикла ссылок.
Если вы не хотите изменять основную логику кода, можно переместить импорт, который вызывает ошибку ImportError, на другое место в файле, например, в конец.
Вот пример:
a.py
from test.b import b2
def a1():
print('a1')
b2()
b.py
from test.a import a1
def b1():
print('b1')
a1()
def b2():
print('b2')
if __name__ == '__main__':
b1()
В этом случае вы получите ошибку импорта: ImportError: cannot import name 'a1'
.
Но если переместить строку from test.b import b2
в a.py
немного ниже, как показано ниже:
a.py
def a1():
print('a1')
b2()
from test.b import b2
Теперь код будет работать так, как задумано:
b1
a1
b2
Таким образом, перестановка импорта позволяет избежать циклической зависимости, и код выполняется корректно.
Импорт файлов из другой папки
Относительные импорты в Python 3
Импорт модуля по относительному пути
Использовать 'import module' или 'from module import'?
Подключение файла из подпапки?