#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