Python类工厂问题引用基类?

2 投票
2 回答
2989 浏览
提问于 2025-04-16 15:52

我最近看到一个关于Python类工厂实现的内容,觉得它非常适合我正在处理的问题。唯一不同的是,我想把基类和子类放在不同的包里。

但是,当我尝试这样做时,每当我想加载基类时,就会遇到问题。

结构如下:

BaseClass.py

from subclasses import *

def NewClass():
    """Map Factory"""
    for cls in BaseClass.__subclasses__():
        print "checking class..."

class BaseClass(object):
    def __init__(self):
        print("Building an abstract BaseMap class..")

subclasses/__init__.py

__all__=['SubClass']

subclasses/SubClass.py

from BaseClass import BaseClass
class SubClassA(BaseClass):
    def __init__(self):
        print('Instantiating SubClassA')

当我尝试导入BaseClass时,却出现了以下错误:

       1 #import BaseClass

 ----> 2 from BaseClass import BaseClass
       3 class SubClassA(BaseClass):
       4     def __init__(self):
       5         print('Instantiating SubClassA')

 ImportError: cannot import name BaseClass

我还尝试过使用“import BaseClass”,然后再去继承“BaseClass.BaseClass”,但这又导致了另一个错误:

      1 import BaseClass
----> 2 class SubClassA(BaseClass.BaseClass):
      3     def __init__(self):
      4         print('Instantiating SubClassA')

AttributeError: 'module' object has no attribute 'BaseClass'

最后,如果我只是尝试创建子类目录,那是没有问题的。只有在我尝试导入BaseClass模块时,事情才会出错。

有没有什么想法?

2 个回答

1

根据你提供的信息,看来你的模块 BaseClass 可能没有直接放在 Python 的搜索路径上。虽然 BaseClass 能找到它下面的子类,因为这些子类在 BaseClass 的下方目录中,但反过来就不行了。

使用一个相对导入可以解决这个问题。

from .. BaseClass import BaseClass

这里的 .. 是用来向上移动一个目录,就像文件路径那样。另一种方法是把 BaseClass 直接放在 PYTHONPATH 中。

不过,老实说,让两个不同的模块互相依赖听起来并不是个好主意。更好的做法是让子类去注册给 BaseClass。

编辑:

我所说的“注册给基类”是指类似下面这样的做法:

# baseclass.py
subclasses = []
def register(cls):
    subclasses.append(cls)


# subclass.py
class SubClassA(BaseClass):
    ...

baseclass.register(SubClassA)

这样一来,BaseClass 就不需要知道所有不同的子类了。子类可以注册自己被使用,BaseClass 在使用它们时也不需要事先了解它们。

2

一些实验表明,问题出在导入的递归上。在BaseClass.py文件中把导入语句做成条件性的,解决了我测试中的问题:

if __name__ == '__main__':
    from subclasses import *

这样可以避免递归,Python似乎对此很满意。

[编辑]

一个更好的解决方案是把NewClass的方法放在一个单独的文件里:

something.py

from BaseClass import BaseClass
from subclasses import *

def NewClass():
    """Map Factory"""
    for cls in BaseClass.__subclasses__():
        print ("checking class...")

NewClass ()

撰写回答