magic imports in Python

Viewed 122

Edit: I should have specified I'm (stuck) working w/ Python 2, but would be interested to see how this could be tackled in either 2 or 3

The scenario: I have a package called shapes.

I have a module in shapes called factory which has a ShapeClassFactory class. This class can be passed a string, and it will look in a remote database for data and use that to dynamically define a class, which it returns.

shapes.py:

from .factory import ShapeClassFactory
__all__ = ['ShapeClassFactory']

In practice this package may be used in a variety of other packages & scripts, like so:

from shapes import ShapeClassFactory

Circle = ShapeClassFactory("Circle")
Rect = ShapeClassFactory("Rect")

myCircle = Circle(r=5, fill='red')
mySquare = Rect(x=5, y=5, fill=None)

The problem: The above is all fine. However, I would love to be able to write the shapes package in such a way that it could be used like so:

from shapes import Circle, Rect

myCircle = Circle(r=5, fill='red')
mySquare = Rect(x=5, y=5, fill=None)

...the idea being that if the member isn't found in shapes, it uses ShapeClassFactory to try and generate it.

The difficulty is that the available classes basically aren't known until requested, so a predefined list of class names won't help.

I don't mind throwing an ImportError if ShapeClassFactory fails to build a class -- but is such a thing even possible?

1 Answers

You can do this by automatically constructing all possible objects in the shapes namespace on initialization, as long as there aren't too many possible classes and the cost of initializing the classes up-front isn't too heavy. You would use code like this in shapes.py:

from .factory import ShapeClassFactory

__all__ = ['ShapeClassFactory']

def get_shape_names():
    """Returns all valid shapes that can be passed in to ShapeClassFactory"""
    return ['Circle', 'Rect']  # your own code should query the database

for name in get_shape_names():
    globals()[name] = ShapeClassFactory(name)
    __all__.append(name)
Related