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

В этой ситуации циклическая ошибка импорта не возникает

Другие вопросы по тегам