4

Below is a pattern from :https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Factory.html . My question is this, is this still the best idiom/pattern to do generic object creation in Python 3.x? I can't seem to find much on this topic. The code is below:

class Shape(object):
    # Create based on class name:
    def factory(type):
        #return eval(type + "()")
        if type == "Circle": return Circle()
        if type == "Square": return Square()
        assert 0, "Bad shape creation: " + type
    factory = staticmethod(factory)

class Circle(Shape):
    def draw(self): print("Circle.draw")
    def erase(self): print("Circle.erase")

class Square(Shape):
    def draw(self): print("Square.draw")
    def erase(self): print("Square.erase")

# Generate shape name strings:
def shapeNameGen(n):
    types = Shape.__subclasses__()
    for i in range(n):
        yield random.choice(types).__name__

shapes = \
  [ Shape.factory(i) for i in shapeNameGen(7)]

for shape in shapes:
    shape.draw()
    shape.erase()

You can also create a factory by using the __class__ method as well I've noticed, but I'm unsure of the best way to use this.

5
  • 2
    I don't see much use for the factory method or shapeNameGen here. Why not just types = Shape.__subclasses__(); shapes = [random.choice(types)() for _ in range(7)]? Why hardcode the strings 'Circle' and 'Square' if you don't have to? The factories for Circle and Square are Circle and Square. Commented Oct 23, 2018 at 11:47
  • I have a situation where I could have multiple geometries being given. This is an over simplified example, but I am getting JSON response of various geometries, and instead of having to write multiple if statements multiple times, I thought using a Factory could reduce the issue: so Shape(JSON) -> Circle or Shape(JSON) - Square, etc... Commented Oct 23, 2018 at 11:53
  • 3
    I still think this factory is awkward.You could have a dictionary {'Circle': Circle, ...} or instantiate a class dynamically by name. Commented Oct 23, 2018 at 11:56
  • @timeb Thanks for the suggestion, I'm going to check out the dynamically by name link. Commented Oct 23, 2018 at 12:14
  • the reason you don’t find much existing info is this pattern isn’t very useful in Python. Now, if you had a complex set of rules that the Shape class had to work out and really didn’t concern the rest of the code, then maybe. As in dumping a chunk of json and letting the factory’s internals decide. But it’s overkill in this case. Also Python classes often struggle a bit knowing about themselves, making this awkward. Commented Oct 25, 2018 at 0:56

1 Answer 1

1

I could be missing something, but I don't like this pattern.

You already have factories for Circle and Square - Circle and Square. :)

The code in your question unnecessarily hardcodes the class names in factory and then goes through some extra hoops by getting the names of the subclasses of Shape and then calling factory with those names.

A more direct way to generate the shapes list is

types = Shape.__subclasses__()
shapes = [random.choice(types)() for _ in range(7)]

I have a situation where I could have multiple geometries being given. This is an over simplified example, but I am getting JSON response of various geometries, and instead of having to write multiple if statements multiple times, I thought using a Factory could reduce the issue: so Shape(JSON) -> Circle or Shape(JSON) - Square

This does not justify the factory as it is coded here. You could have a simple dictionary like

classes = {'Circle': Circle, 'Square': Square, ...}

or possibly create it dynamically with

classes = {cls.__name__:cls for cls in Shape.__subclasses__()}

and then call classes[some_string]() for the instantiation. You can even dynamically instantiate a class by string name using getattr.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.