Python:类型检查需要循环导入
首先,我知道关于循环导入的问题已经有很多问答了。
简单来说,答案就是:“好好设计你的模块或类结构,就不需要循环导入。”这话没错。我在当前项目中努力设计,觉得自己做得还不错。
但我遇到的具体问题是:我需要在一个模块中进行类型检查,而这个模块已经被包含类的模块导入了。这就导致了导入错误。
就像这样:
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”这个例子,我想把它们分开到不同的模块文件中。
既然我自己的两个解决方案都不适合我:有没有人能提供一个更好的解决方案呢?
4 个回答
你可以在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()
你可以通过编程来使用 interface
(在 Python 中叫做 ABC,即 抽象基类),而不是直接使用具体的类型 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()
最好的办法就是不检查类型。
另一种解决方案是,在两个类都加载之前,不去创建或引用 Foo
或 Bar
。如果第一个模块先加载,就等到 class Foo
这行代码执行完后,再去创建或引用 Bar
。同样的,如果第二个模块先加载,就等到 class Bar
这行代码执行完后,再去创建或引用 Foo
。
这其实就是 ImportError
的根源。如果你改成先 "import foo" 和 "import bar",然后在需要用到 Foo
的地方用 foo.Foo
,在需要用到 Bar
的地方用 bar.Bar
,就能避免这个问题。这样做的话,直到创建 Foo
或 Bar
之前,你都不会去引用它们,这样就能确保在两个都创建完之后再使用它们(否则你会遇到 AttributeError
的错误)。