首先:我知道已经有很多关于循环进口主题的问题和答案。
答案或多或少:“正确设计模块/类结构,不需要循环导入”。那是真实的。我努力为我当前的项目做一个合适的设计,我认为我成功了。
但我的具体问题如下:我需要在模块中进行类型检查,该模块已由包含要检查的类的模块导入。但是这会引发导入错误。
像这样:
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:如果我修改它以通过字符串比较检查类型,它将起作用。但我真的不喜欢这个解决方案(对于简单的类型检查,字符串比较相当昂贵,并且在重构时可能会遇到问题。)
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:我还可以将这两个类打包到一个模块中。但我的项目有许多不同的类,如“Bar”示例,我想将它们分成不同的模块文件。
在我自己的2个解决方案之后对我来说没有选择:有没有人为这个问题找到更好的解决方案?
答案 0 :(得分:5)
最好的解决方案是不检查类型。
另一个解决方案是在两个类都加载之前不创建Foo
或Bar
的实例,而不是它们。如果首先加载第一个模块,则在执行Bar
语句之前不要创建Bar
或引用class Foo
。同样,如果先加载第二个模块,则在执行Foo
语句之前不要创建Foo
或引用class Bar
。
这基本上是ImportError
的来源,如果你改为“导入foo”和“导入栏”,可以避免这种情况,并使用foo.Foo
现在使用Foo
和bar.Bar
现在使用Bar
。在执行此操作时,在创建Foo
或Bar
之前,您不再引用它们中的任何一个,这有希望在两者都创建之后才会发生(否则您将获得{{1} }})。
答案 1 :(得分:5)
您可以针对interface
(python中的ABC - abstract base class)进行编程,而不是特定类型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()
答案 2 :(得分:2)
你可以按照这样的方式推迟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()
答案 3 :(得分:0)
可能的重复项:Python type hinting without cyclic imports
您应该使用Forward Reference(PEP 484-类型提示):
当类型提示包含尚未定义的名称时,该定义可以表示为字符串文字,以便稍后解析。
所以代替:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
这样做:
class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
self.left = left
self.right = right