В первую очередь: Я действительно знаю, что уже существует много вопросов и ответов на тему кругового импорта.
Ответ более или менее: "Разработайте свою структуру Модуля/Класса правильно, и Вам не будет нужен круговой импорт". Это верно. Я пытался очень трудно сделать надлежащий дизайн для своего текущего проекта, я, по-моему, я был успешен с этим.
Но моя определенная проблема следующая: Мне нужна регистрация типа модуля, который уже импортируется модулем, содержащим класс для проверки по. Но это бросает ошибку импорта.
Как так:
foo.py:
from bar import Bar
class Foo(object):
def __init__(self):
self.__bar = Bar(self)
bar.py:
from foo import Foo
class Bar(object):
def __init__(self, arg_instance_of_foo):
if not isinstance(arg_instance_of_foo, Foo):
raise TypeError()
Решение 1: Если я изменил его для проверки типа сравнением строк, это будет работать. Но мне действительно не нравится это решение (представьте comparsion в виде строки, является довольно дорогим для простой проверки типа и мог получить проблему когда дело доходит до рефакторинга).
bar_modified.py:
from foo import Foo
class Bar(object):
def __init__(self, arg_instance_of_foo):
if not arg_instance_of_foo.__class__.__name__ == "Foo":
raise TypeError()
Решение 2: Я мог также упаковать эти два класса в один модуль. Но мой проект имеет много различных классов как пример "Панели", и я хочу разделить их в различные файлы модуля.
После того, как моими собственными 2 решениями не является никакая опция для меня: у Кого-либо есть более хорошее решение для этой проблемы?
Лучшее решение - не проверять типы.
Другое решение - не создавать экземпляр и вообще не ссылаться на Foo
или Bar
, пока оба класса не будут загружены. Если первый модуль загружен первым, не создавайте Bar
и не ссылайтесь на Bar
до тех пор, пока не будет выполнен оператор class Foo
. Аналогично, если второй модуль загружается первым, не создавайте Foo
и не ссылайтесь на Foo
до тех пор, пока не будет выполнен оператор class Bar
.
Это в основном источник ImportError
, которого можно было бы избежать, если бы вы сделали "import foo" и "import bar" вместо этого, и использовали foo.Foo
там, где вы сейчас используете Foo
, и bar.Bar
там, где вы сейчас используете Bar
. При этом вы больше не ссылаетесь ни на один из них, пока не будет создан Foo
или Bar
, что, надеюсь, произойдет только после создания обоих (иначе вы получите AttributeError
).
Вы можете программировать против интерфейс
(ABC - абстрактный базовый класс в python), а не конкретный тип Bar
. Это классический способ разрешения взаимозависимостей пакета / модуля на многих языках. Концептуально это также должно привести к лучшему проектированию объектной модели.
В вашем случае вы должны определить интерфейс IBar
в каком-то другом модуле (или даже в модуле, содержащем класс Foo - в зависимости от использования этого abc
). Ваш код будет выглядеть так:
foo.py:
from bar import Bar, IFoo
class Foo(IFoo):
def __init__(self):
self.__bar = Bar(self)
# todo: remove this, just sample code
f = Foo()
b = Bar(f)
print f
print b
x = Bar('do not fail me please') # this fails
bar.py:
from abc import ABCMeta
class IFoo:
__metaclass__ = ABCMeta
class Bar(object):
def __init__(self, arg_instance_of_foo):
if not isinstance(arg_instance_of_foo, IFoo):
raise TypeError()
Вы можете просто отложить импорт в bar.py следующим образом:
class Bar(object):
def __init__(self, arg_instance_of_foo):
from foo import Foo
if not isinstance(arg_instance_of_foo, Foo):
raise TypeError()