Search code examples
pythonfactoryinstantiation

Python factory dynamic instantiation


I am a C# programmer, and I am looking to make a simple factory. Observe the 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)

The issue is that Create will always return the same instance of, say, Circle. How can I implement a means to dynamically instantiate an object, while allowing OCP?

Edit: To further extend on my point; in C#, I would declare my dictionary as:

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

This will always return a newly instantiated object for each value of the dictionary. This is the effect I wish to reproduce in python.


Solution

  • You've got some larger issues in this design.

    First off if you have a mutable attribute in the class, you should define _registry in the __init__ method of the class (isolated to the instance).

    Next, the type attribute to _registry is wrong. You have a dict of lambda, not of Shape instances. You need Dict[ShapeType, Callable[[], Shape]]. The Register method has a similar problem.

    You should also follow PEP 8 rules for naming. Try this:

    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)