我正在尝试为Python 3.5实现一个小型库,但一直在努力解决如何正确处理包/模块的结构以及如何使导入工作。
我一直遇到python抱怨无法导入某些名称的问题,如
ImportError: cannot import name 'SubClass1'
当“SubClass1”需要导入其他模块但其他模块也需要了解SubClass1(循环导入)时,似乎会发生这种情况。 我需要在我的库中循环导入,因为基类有一个工厂方法,可以创建正确的子类实例(还有其他需要循环导入的情况,例如检查函数参数的类型需要导入该类型的位置已定义,但该模块本身可能需要完成该检查的类:另一个循环依赖!)
以下是示例代码:
根目录包含子目录dir1。目录dir1包含空文件 init .py,文件baseclass.py和文件subclass1.py。 文件./dir1/subclass1.py包含:
from . baseclass import BaseClass
class SubClass1(BaseClass):
pass
文件./dir1/baseclass.py包含:
from . subclass1 import SubClass1
class BaseClass(object):
def make(self,somearg):
# .. some logic to decide which subclass to create
ret = SubClass1()
# .. which gets eventually returned by this factory method
return ret
文件./test1.py包含:
from dir1.subclass1 import SubClass1
sc1 = SubClass1()
这会导致以下错误:
Traceback (most recent call last):
File "test1.py", line 1, in <module>
from dir1.subclass1 import SubClass1
File "/data/johann/tmp/python1/dir1/subclass1.py", line 1, in <module>
from . baseclass import BaseClass
File "/data/johann/tmp/python1/dir1/baseclass.py", line 1, in <module>
from . subclass1 import SubClass1
ImportError: cannot import name 'SubClass1'
解决此问题的标准/最佳方法是什么,理想情况是向后兼容python 2.x和python 3到版本3.2?
我在其他地方读过,导入模块而不是模块中的某些内容可能会有所帮助,但我不知道如何以相对方式导入模块(例如,subclass1),因为“import.subslass1”或类似方法不起作用
答案 0 :(得分:3)
您的问题是由循环导入引起的。 baseclass
模块正尝试从SubClass1
模块导入subclass1
,但subclass
正在尝试右后导入BaseClass
。得到NameError
因为在import
语句运行时尚未定义类。
有几种方法可以解决这个问题。
一种选择是改变你的导入方式。不要按名称导入类,而是导入模块,稍后再将名称作为属性查找。
from . import baseclass
class SubClass1(baseclass.BaseClass):
pass
和
from . import subclass1
class BaseClass:
def make(self,somearg):
# ...
ret = subclass1.SubClass1()
由于SubClass1
需要能够在定义时立即使用BaseClass
,因此如果在baseclass
之前导入subclass1
模块,则此代码可能仍会失败。所以这不太理想
另一种选择是将baseclass
更改为在BaseClass
定义之下执行导入。这样,subclass
模块将能够在需要时导入名称:
class BaseClass:
def make(self,somearg):
# .. some logic to decide which subclass to create
ret = SubClass1()
from .subclass1 import SubClass1
这并不理想,因为放置导入的正常位置位于文件的顶部。将它们放在别处会使代码更加混乱。您可能希望在文件顶部添加注释,说明如果您沿着这条路线延迟导入的原因。
另一种选择可能是将两个模块合并为一个文件。 Python并不要求每个类都像其他语言一样拥有自己的模块。当你有紧密耦合的类(比如你的例子中的类)时,将它们放在一个地方是很有意义的。这样可以避免整个问题,因为您根本不需要任何导入。
最后,还有一些更复杂的解决方案,比如依赖注入。而不是基类需要知道子类,每个子类可以通过调用一些函数并将引用传递给它自己来注册自己。例如:
# no imports of subclasses!
def BaseClass:
subclasses = []
def make(self, somearg):
for sub in self.subclasses:
if sub.accepts(somearg):
return sub()
raise ValueError("no subclass accepts value {!r}".format(somearg))
@classmethod
def register(cls, sub):
cls.subclasses.append(sub)
return sub # return the class so it can be used as a decorator!
并在subclass.py
from .baseclass import BaseClass
@BaseClass.register
class SubClass1(BaseClass):
@classmethod
def accepts(cls, somearg):
# put logic for picking this subclass here!
return True
这种编程风格稍微复杂一点,但它可能很好,因为它比BaseClass
需要预先知道所有子类的版本更容易扩展。您可以通过多种方式实现此类代码,使用register
函数只是其中之一。关于它的一个好处是它并不严格要求继承(因此如果你愿意,你可以注册一个实际上不会从BaseClass
继承的类)。如果您只处理实际的继承子类,您可能需要考虑使用一个自动为您完成所有子类注册的元类。