11

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

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

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

像这样:

foo.py:

from bar import Bar

class Foo(object):

    def __init__(self):
        self.__bar = Bar(self)

酒吧.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

4 回答 4

7

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

另一种解决方案是不创建实例,根本不引用,Foo或者Bar直到两个类都被加载。如果首先加载第一个模块,则在执行语句之前不要创建Bar或引用。同样,如果先加载第二个模块,则在执行语句之前不要创建或引用。Barclass FooFooFooclass Bar

这基本上是 的来源,如果您改为使用“import foo”和“import bar”,并在您现在使用的地方和现在使用的地方使用,ImportError则可以避免这种情况。在执行此操作时,在创建or之前您不再引用它们中的任何一个,希望在创建两者之后才会发生这种情况(否则您将得到一个)。foo.FooFoobar.BarBarFooBarAttributeError

于 2010-03-17T11:59:05.530 回答
6

您可以针对interface(ABC - python 中的抽象基类Bar)进行编程,而不是针对特定类型。这是解决许多语言中包/模块相互依赖关系的经典方法。从概念上讲,它还应该导致更好的对象模型设计。

在您的情况下,您将在其他模块中定义接口IBar(甚至在包含 Foo 类的模块中 - 取决于 that 的用法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

酒吧.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()
于 2010-03-17T12:10:08.037 回答
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()
于 2010-03-17T11:59:47.390 回答
2

可能重复:没有循环导入的 Python 类型提示

您应该使用前向参考(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
于 2019-07-13T07:11:19.613 回答