Ответ 1
Только импортируйте модуль, не импортируйте его из модуля:
Рассмотрим a.py
:
import b
class A:
def bar(self):
return b.B()
и b.py
:
import a
class B:
def bar(self):
return a.A()
Это прекрасно работает.
Я знаю, что проблема циклического импорта в python возникла много раз раньше, и я прочитал эти обсуждения. Комментарий, который неоднократно повторяется в этих обсуждениях, заключается в том, что циклический импорт является признаком плохой конструкции, и код должен быть реорганизован, чтобы избежать циклического импорта.
Может ли кто-нибудь сказать мне, как избежать циклического импорта в этой ситуации?: У меня есть два класса, и я хочу, чтобы каждый класс имел конструктор (метод), который принимает экземпляр другого класса и возвращает экземпляр класса.
Более конкретно, один класс является изменяемым, а один неизменным. Требуется неизменный класс для хэширования, сравнения и т.д. Переменный класс необходим, чтобы что-то делать. Это похоже на наборы и фризонсет, или на списки и кортежи.
Я мог бы поместить оба определения классов в один и тот же модуль. Есть ли другие предложения?
Примером игрушки может быть класс A, который имеет атрибут, который является списком и классом B, который имеет атрибут, который является кортежем. Затем класс A имеет метод, который принимает экземпляр класса B и возвращает экземпляр класса A (путем преобразования кортежа в список), и аналогично класс B имеет метод, который принимает экземпляр класса A и возвращает экземпляр класса B (путем преобразования списка в кортеж).
Только импортируйте модуль, не импортируйте его из модуля:
Рассмотрим a.py
:
import b
class A:
def bar(self):
return b.B()
и b.py
:
import a
class B:
def bar(self):
return a.A()
Это прекрасно работает.
Рассмотрим следующий пример пакета Python, в котором a.py
и b.py
зависят друг от друга:
/package
__init__.py
a.py
b.py
Круговые зависимости импорта обычно делятся на две категории в зависимости о том, что вы пытаетесь импортировать и где вы используете его внутри каждого модуль. (И используете ли вы Python 2 или 3).
В некоторых случаях просто импортируйте модуль с циклической зависимостью импорта может привести к ошибкам, даже если вы ничего не ссылаетесь на импортированный модуль.
Есть несколько стандартных способов импортировать модуль в Python
import package.a # (1) Absolute import
import package.a as a_mod # (2) Absolute import bound to different name
from package import a # (3) Alternate absolute import
import a # (4) Implicit relative import (deprecated, python 2 only)
from . import a # (5) Explicit relative import
К сожалению, только 1-й и 4-й варианты действительно работают, когда вы
имеют круговые зависимости (остальные все поднимают ImportError
или AttributeError
). В общем, вы не должны использовать
4-й синтаксис, так как он работает только в Python2 и рискует
конфликт с другими сторонними модулями. Так реально только первый
синтаксис гарантированно работает.
ОБНОВЛЕНИЕ: проблемы
ImportError
иAttributeError
возникают только в Python 2. В Python 3 была переписана импортная техника и все из этих операторов импорта (за исключением 4) будет работать, даже с круговые зависимости. Решения в этом разделе предназначены для людей, использующих Python 2.
Просто используйте первый синтаксис импорта выше. Недостатком этого метода является что имена импорта могут быть очень длинными для больших пакетов.
В a.py
import package.b
В b.py
import package.a
Я видел этот метод, используемый во многих пакетах, но он все еще чувствует мне повезло, и мне не нравится, что я не могу посмотреть на верхнюю часть модуля и увидеть все его зависимости, я должен искать все функции также.
В a.py
def func():
from package import b
В b.py
def func():
from package import a
Это также работает, но имеет ту же проблему, что и первый метод, где все пакетные и субмодульные звонки становятся очень длинными. У этого также есть два основные недостатки - это заставляет импортировать все подмодули, даже если вы используете только один или два, и вы все еще не можете смотреть на любой из субмодули и быстро увидеть их зависимости в верхней части, вы должны просеять функции.
В __init__.py
from . import a
from . import b
В a.py
import package
def func():
package.b.some_object()
В b.py
import package
def func():
package.a.some_object()
Теперь, хотя вы можете импортировать модуль с циклическим импортом зависимость, вы не сможете импортировать объекты, определенные в модуле или на самом деле сможет ссылаться на этот импортированный модуль в любом месте в верхнем уровне модуля, куда вы его импортируете. Ты можешь, однако используйте импортированный модуль внутри функций и блоков кода, которые не запустить при импорте.
Например, это будет работать:
пакет/a.py
import package.b
def func_a():
return "a"
пакет/b.py
import package.a
def func_b():
# Notice how package.a is only referenced *inside* a function
# and not the top level of the module.
return func_a() + "b"
Но это не сработает
пакет/a.py
import package.b
class A(object):
pass
пакет/b.py
import package.a
# package.a is referenced at the top level of the module
class B(package.a.A):
pass
Вы получите исключение
AttributeError: модуль 'package' не имеет атрибута 'a'
Как правило, в большинстве действительных случаев циклических зависимостей возможно реорганизовать или реорганизовать код для предотвращения этих ошибок и перемещения ссылки на модули внутри блока кода.
Мы выполняем комбинацию абсолютного импорта и функций для лучшего чтения и более коротких строк доступа.
главный/подчиненный/a.py
import main.sub.b
b_mod = lambda: main.sub.b
class A():
def __init__(self):
print('in class "A":', b_mod().B.__name__)
главный/подчиненный/b.py
import main.sub.a
a_mod = lambda: main.sub.a
class B():
def __init__(self):
print('in class "B":', a_mod().A.__name__)