我正在尝试为python 3.5实现一个小库,但是一直在努力正确处理包装/模块的结构以及如何使导入工作的工作。
我一直遇到python抱怨无法以错误导入某些名称的问题,例如
ImportError: cannot import name 'SubClass1'
当" subclass1"需要导入其他模块时,这似乎会发生,但是其他模块还需要了解subclass1(循环导入)。我需要在库中进行周期导入,因为基类具有一种工厂方法,可以创建适当的子类实例(在其他情况下需要循环导入,例如检查函数参数的类型需要该类型的导入到何处定义,但是该模块本身可能需要进行检查的地方:另一个环状依赖性!)
这是示例代码:
根目录包含子目录DIR1。目录dir1包含和空文件 init .py,file 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),因为"导入。。
您的问题是由循环导入引起的。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
继承的类)。如果您仅处理实际继承子类,则可能需要考虑使用自动为您的所有子类注册的元素。