Проблема с фабрикой классов Python, ссылающаяся на базовый класс?

Недавно я наткнулся на реализацию фабрики классов 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..")

подклассы / __ init__.py

__all__=['SubClass']

подклассы / 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, все идет не так.

Любые идеи?


person Keith Hughitt    schedule 16.04.2011    source источник
comment
другая ошибка.? Какая другая ошибка? Включите также эту ошибку. Кроме того, ваш отступ кажется неправильным в примере BaseClass.py. Пожалуйста, внимательно проверьте это. Весь код должен иметь отступ. И предваряется пустой строкой. У вас есть from subclasses import *, который может быть кодом, но также может быть частью вопроса.   -  person S.Lott    schedule 16.04.2011
comment
Спасибо, С. Лотт. Я исправил упомянутые вами проблемы. Первоначально я думал, что добавление второй ошибки может быть слишком много, поэтому я не стал это делать. Выход сейчас.   -  person Keith Hughitt    schedule 16.04.2011
comment
добавление второй ошибки может оказаться слишком большим? На самом деле это невозможно. Если у вас возникла другая ошибка, вы должны задокументировать ее. Все остальное недопустимо, потому что вы пропустили часть проблемы.   -  person S.Lott    schedule 18.04.2011


Ответы (2)


Некоторые эксперименты показывают, что проблема заключается в рекурсии импорта. Создание условного оператора импорта в BaseClass.py решило проблему для моих тестов:

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

предотвращает рекурсию, и питон, похоже, этим доволен.

[РЕДАКТИРОВАТЬ]

Лучшее решение - поместить метод NewClass в отдельный файл:

something.py

from BaseClass import BaseClass
from subclasses import *

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

NewClass ()
person Anton    schedule 16.04.2011
comment
Привет, Антон, спасибо за предложение. К сожалению, это предотвращает ошибку, но нарушает фабрику классов. Например, если вы затем попытаетесь вызвать BaseClass.NewClass (), ничего не произойдет, потому что подклассы не были импортированы. - person Keith Hughitt; 16.04.2011
comment
@Keith: В этом случае ваш дизайн не работает, попробуйте распутать код, чтобы между одним модулем и другим было как можно меньше зависимости. - person XORcist; 16.04.2011
comment
Помещение функции и базового класса в отдельные файлы помогло. Спасибо за помощь :) - person Keith Hughitt; 16.04.2011

Без дополнительной информации кажется, что ваш модуль 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 может использовать их, даже не зная о них заранее.

person Josh Smeaton    schedule 16.04.2011
comment
Привет, Джош, какую еще информацию ты мог бы использовать? Вышеупомянутое должно описывать весь код и структуру файлов / каталогов. Что вы имеете в виду, регистрируя подклассы с помощью BaseClass? Кроме того, я попробовал относительный импорт, но получил новую ошибку: ValueError: Попытка относительного импорта за пределами пакета верхнего уровня. - person Keith Hughitt; 16.04.2011