Python3: неявный импорт из подмодулей, избегая рекурсивного импорта
Представьте себе следующий сценарий. Вы пишете какой-то модуль, состоящий из нескольких файлов. Когда вы пишете код, в конечном итоге вы попадаете в ситуацию, когда несколько файлов (скажем, main.py
а также side.py
) импортировать друг друга, что приводит к рекурсивному импорту, запрету.
module/
main.py
side.py
Вы решили разделить main
в несколько файлов: base.py
а также advanced.py
, Первый содержит только основные определения из main.py
и не использует side
ни какие-либо другие подмодули; другие подмодули, которые хотят использовать main
должно быть удовлетворено импортированием этого. Последний (advanced.py
) может свободно импортировать что угодно из side
, но с тех пор side
не использует advanced
нет рекурсии Это разрешает рекурсивный импорт.
Теперь у вас есть следующая структура пакета:
module/
side.py
base.py
advanced.py
Имеет смысл поставить base
а также advanced
в подпапку main
(таким образом создавая подмодуль), потому что это по крайней мере частично сохраняет исходную структуру пакета. Таким образом, мы получаем
module/
side.py
main/
base.py
advanced.py
Но теперь рассмотрим третий файл third.py
который изначально импортировал весь main
:
from .main import *
Интерфейс для main
был нарушен вышеупомянутым оператором "исправления рекурсии". Итак, как восстановить исходный интерфейс, то есть как сделать from .main import *
импортировать все из обоих base
а также advanced
?
пример
оригинал main.py
:
from .side import *
class A:
pass
class B(C):
pass
оригинал side.py
:
from .main import *
class C:
pass
class D(A):
pass
После реструктуризации, main
делится на два файла:
# base.py
class A:
pass
# advanced.py
from module.side import *
class B(C):
pass
А также side
теперь должен импортировать main.base
вместо main
:
# new side.py
from .main.base import *
class C:
pass
class D(A):
pass
Вот некоторые не совсем удовлетворительные решения / идеи:
__init__.py
shenaningans
Создайте __init__.py
в main
субмодуль, и поместите в него следующее (решение из неявного импорта из субмодулей):
from .base import *
from .advanced import *
Проблема в том, что снова вводится рекурсивный импорт: напомним, что side
использует main.base
таким образом где-то там есть строка кода
from .main.base import *
который будет ссылаться на __init__.py
, вызывая side
импортировать из advanced
также. Которого мы хотели избежать. Точнее говоря, поместив следующее в интерпретатор Python
from module.side import *
выводит следующую ошибку:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File ".../module/side.py", line 2, in <module>
from .main.base import *
File ".../module/main/__init__.py", line 2, in <module>
from .advanced import *
File ".../module/main/advanced.py", line 4, in <module>
class B(C):
NameError: name 'C' is not defined
Причина в том, что при загрузке advanced
, он пытается загрузить side
, Но с тех пор side
"уже загружен" (или, скорее, загружается в настоящее время), чтобы избежать бесконечной рекурсии, python просто пропускает его. Но тогда класс C
, который требуется advanced
не загружен.
Вместо этого положите shenaningans в all.py
Вместо того, чтобы поместить вышеупомянутые две строки кода в __init__.py
, поместите это в другой файл all.py
, Теперь, когда кто-то хочет импортировать все из main
один пишет
from .main.all import *
Это все еще не то же самое, что from .main import *
поэтому, когда бы происходила такая "рекурсивно-фиксирующая" реструктуризация пакета, нужно было бы искать все такие импорты и переписывать их (добавляя .all
).
1 ответ
Во многих случаях этот метод работает. что вы импортируете объект модуля внутри блока, например, если side.py использует main.A, а main.py использует side.C, вы можете определить простую функцию в каждом модуле и импортировать A или C внутри него, а функция возвращает класс C или A.
в main.py:
def get_C():
from .side import C
return C
class A:
pass
class B(get_C()):
pass
и в side.py:
def get_A():
from .main import A
return A
class C:
pass
class D(get_A()):
pass
В этой ситуации циклическая ошибка импорта не возникает