Динамическое создание экземпляра Python factory

#python #factory #instantiation

#python #фабрика #создание экземпляра

Вопрос:

Я программист на C #, и я хочу создать простую фабрику. Обратите внимание на appempt:

 class ShapeFactory:
    _registry: Dict[ShapeType, Shape] = {
        ShapeType.Shape: lambda: Shape(),
        ShapeType.Circle: lambda: Circle(),
        ShapeType.Square: lambda: Square()
    }

    def Create(self, key: ShapeType) -> Shape:
        if key in self._registry:
            return self._registry.get(key)
        raise KeyError(key)

    def Register(self, key: ShapeType, value: Shape) -> None:
        if key not in self._registry:
            self._registry[key] = value
        raise KeyError(type)
  

Проблема в том, что Create всегда будет возвращаться один и тот же экземпляр, скажем, Circle . Как я могу реализовать средство для динамического создания экземпляра объекта, разрешив OCP?

Редактировать: для дальнейшего расширения моей точки зрения; в C # я бы объявил свой словарь как:

 Dictionary<ShapeType, Func<Shape>> _registry = new Dictionary{
    [ShapeType.Shape] = () => new Shape(),
    [ShapeType.Circle] = () => new Circle(),
    [ShapeType.Square] = () => new Square()
}
  

Это всегда будет возвращать новый экземпляр объекта для каждого значения словаря. Это эффект, который я хочу воспроизвести в python.

Комментарии:

1.Если вы вызываете Circle() n times, вы получите n разные экземпляры

2. если я использую (ShapeType — это перечисление) Create(ShapeType.Circle) , он всегда будет возвращать ОДИН И ТОТ ЖЕ экземпляр. Я подтвердил это с помощью x is y .

3. Я думаю, это может быть из _registry -за того, что вы определили. Нужно попробовать что-то вроде переключателя, чтобы посмотреть, работает ли это?

4. @AbhinavMathur если я использую переключатель, он нарушает OCP. Мне нужно, чтобы он был динамичным и открытым для расширения. Оператор switch выполнит эту работу, потому что он запустит новый путь кода, но это НЕ то, о чем я прошу.

5. На самом деле вы не вызывали конструктор, вы тестируете лямбда-выражение. Вы хотите self._registry.get(key)()

Ответ №1:

У вас есть несколько более серьезных проблем в этом дизайне.

Прежде всего, если у вас есть изменяемый атрибут в классе, вы должны определить _registry его в __init__ методе класса (изолированном для экземпляра).

Далее, атрибут type to _registry неверен. У вас есть dict of lambda , а не Shape экземпляров. Вам нужно Dict[ShapeType, Callable[[], Shape]] . Register Метод имеет аналогичную проблему.

Вы также должны следовать правилам PEP 8 для именования. Попробуйте это:

 class ShapeFactory:
    def __init__(self) -> None:
        self._registry: Dict[ShapeType, Callable[[], Shape]] = {
            ShapeType.Shape: lambda: Shape(),
            ShapeType.Circle: lambda: Circle(),
            ShapeType.Square: lambda: Square()
        }

    def create(self, key: ShapeType) -> Shape:
        if key in self._registry:
            return self._registry[key]()
        else:
            raise KeyError(key)

    def register(self, key: ShapeType, value: Callable[[], Shape]) -> None:
        if key not in self._registry:
            self._registry[key] = value
        else:
            raise KeyError(key)
  

Комментарии:

1. Большое вам спасибо за эти подробности! Я программист на C #, так что это очень помогает!

2. @TimeTravelPenguin Рад помочь!

3. @TimeTravelPenguin кстати, вы можете быстро обнаруживать ошибки такого типа с помощью таких инструментов, как mypy . Это предупредило бы вас о неправильном вводе dict. Я запускаю его во всех своих CI для Python вместе с black , flake8 и pytest

Ответ №2:

Простым питоническим способом динамического создания объектов является:

 class C:
    def __init__(self):
        self.name = "C"
class D:
    def __init__(self):
        self.name = "D"

dic = {"C": C, "D": D}
x1 = dic["C"]()
x2 = dic["C"]()
print(x1 is x2)    #prints False