Python 3.5“ImportError:无法导入名称'SomeName'

时间:2017-12-19 19:03:13

标签: python python-3.x python-2.7

我正在尝试为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”或类似方法不起作用

1 个答案:

答案 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继承的类)。如果您只处理实际的继承子类,您可能需要考虑使用一个自动为您完成所有子类注册的元类。