wil3
wil3

Reputation: 2957

Program structure for a database of callable Python objects

I am working on a project that requires a large database of python objects, each of which has exactly two methods: __init__ and __call__. The init defines attributes specific to each object, while the call method contains the definition of a function parametrized by the attributes. Right now, I am representing this database using a large .py file that contains all of the objects. For example, first two entries in the file myfuncs.py are something like

class Alice:
    def __init__(self, a=10, b=4):
        self.a = a
        self.b = b
        self.color = "red"

    def __call__(self, X):
        return X**2 + self.a + self.b

class Timmy:
    def __init__(self, h=1):
        self.a = a
        self.color = "blue"    

    def __call__(self, X):
        return X**4*self.h

I might use these classes in a separate program:

from myfuncs import Alice
model = Alice(a=10)
y = model(15)
print("The value was ", str(y), " and the color is ", model.color)

Notice that the init method for both objects contains a hard-coded attribute color and a value that is specific to that object. All objects in the database have this attribute, and currently the only way to associate this attribute with the object is through a hard-coded value in the constructor.

Rather than having one long .py file with a bunch of objects (and a lot of boilerplate code), it seems to me like there should be some way to define a database of objects, each with an attribute color and another attribute function that contains the contents of the __call__() method and default keyword arguments. However, after importing a specific object I need it to be treated like a native Python function, and so I'm not sure that storing the functions as strings and then using exec will work.

While my minimal example may seem contrived, in my application these objects contain in their call methods complex mathematical formula written as Python functions, the keyword args are the parameter values in these formulae, and the property color is a label associated with each formula that denotes its symmetry group (which is something I know about each function from external info).

What is the best way to structure my project?

Upvotes: 0

Views: 151

Answers (1)

Myke Bilyanskyy
Myke Bilyanskyy

Reputation: 678

Okay, here's a slightly better way that gets rid of a lot of boilerplate:

from abc import abstractmethod, ABC


class BaseModel(ABC):
    color: str

    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

    @abstractmethod
    def __call__(self, x):
        ...


class Alice(BaseModel):
    color = 'red'
    b = 4

    def __call__(self, x):
        return x ** 2 + self.a + self.b


class Timmy(BaseModel):
    color = 'blue'
    h = 1

    def __call__(self, x):
        return x ** 4 * self.h

This way you only need to define the parts that change from class to class.

Also you can express your print like this:

print(f"The value was {y} and the color is {model.color}")

Upvotes: 1

Related Questions