代码之家  ›  专栏  ›  技术社区  ›  Philip Daubmeier

python:类型检查需要循环导入

  •  8
  • Philip Daubmeier  · 技术社区  · 14 年前

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

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

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

    像这样:

    PY.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(棒修改后.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”示例,我想将它们分为不同的模块文件。

    在我自己的两个解决方案之后,我就没有选择了:有人能更好地解决这个问题吗?

    3 回复  |  直到 14 年前
        1
  •  5
  •   Devin Jeanpierre    14 年前

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

    另一种解决方案是不创建实例,也不引用, Foo Bar 直到两个类都被加载。如果首先加载第一个模块,则不要创建 酒吧 或指 酒吧 直到之后 class Foo 语句被执行。同样,如果第一个加载第二个模块,则不要创建 或参考 直到之后 class Bar 语句被执行。

    这基本上是 ImportError ,如果您改为“导入foo”和“导入栏”,则可以避免,并使用 foo.Foo 你现在使用的地方 bar.Bar 你现在使用的地方 酒吧 . 在执行此操作时,在 酒吧 是被创建的,希望在两者都被创建之前不会发生(否则您将得到 AttributeError )

        2
  •  5
  •   van    14 年前

    你可以计划反对 interface (ABC) abstract base class 在python中),而不是特定类型 Bar . 这是解决多种语言中包/模块之间依赖关系的经典方法。从概念上讲,它还应该导致更好的对象模型设计。

    在您的案例中,您将定义接口 IBar 在其他一些模块中(甚至在包含foo类的模块中),这取决于它的用法 abc )您的代码如下所示:

    PY.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()
    
        3
  •  2
  •   jensq    14 年前

    您可以这样推迟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()