Python:类型检查所需的循环导入

时间:2010-03-17 11:50:44

标签: python import circular-dependency

首先:我知道已经有很多关于循环进口主题的问题和答案。

答案或多或少:“正确设计模块/类结构,不需要循环导入”。那是真实的。我努力为我当前的项目做一个合适的设计,我认为我成功了。

但我的具体问题如下:我需要在模块中进行类型检查,该模块已由包含要检查的类的模块导入。但是这会引发导入错误。

像这样:

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个解决方案之后对我来说没有选择:有没有人为这个问题找到更好的解决方案?

4 个答案:

答案 0 :(得分:5)

最好的解决方案是不检查类型。

另一个解决方案是在两个类都加载之前不创建FooBar的实例,而不是它们。如果首先加载第一个模块,则在执行Bar语句之前不要创建Bar或引用class Foo。同样,如果先加载第二个模块,则在执行Foo语句之前不要创建Foo或引用class Bar

这基本上是ImportError的来源,如果你改为“导入foo”和“导入栏”,可以避免这种情况,并使用foo.Foo现在使用Foobar.Bar现在使用Bar。在执行此操作时,在创建FooBar之前,您不再引用它们中的任何一个,这有希望在两者都创建之后才会发生(否则您将获得{{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