Bryan_D
Bryan_D

Reputation: 563

Defining properties to be used in a subset of child classes

I am writing an API client for a RESTful web service. The problem I am having is one of the objects returned by the API has many subtypes. In total, there are over 200 properties across the subtypes, some being duplicates.

Those that are used by all subtypes I've defined in the parent class. However, there are some properties that only get used by, let's say, 5 of the children. In a normal case, I would just add another layer to the parent/child model, but children mix and match which properties they have in common. Here's an example

class Parent(object):
    @property
    def apiLinks(self):
        pass

    @apiLinks.setter
    def apiLinks(self, value):
        pass

class Child1(Parent):
    @property
    def interval(self):
        pass

    @interval.setter
    def interval(self, value):
        pass

    @property
    def duration(self):
        pass

    @apiLinks.setter
    def duration(self, value):
        pass


class Child2(Parent):
    @property
    def interval(self):
        pass

    @interval.setter
    def interval(self, value):
        pass

    @property
    def prefix(self):
        pass

    @prefix.setter
    def prefix(self, value):
        pass


class Child3(Parent):
    @property
    def duration(self):
        pass

    @duration.setter
    def duration(self, value):
        pass

    @property
    def prefix(self):
        pass

    @prefix.setter
    def prefix(self, value):
        pass

As you can see:

I don't want to define the same setter/getter functions more than once and same with the docstrings.

I've looked into importing properties, but that doesn't seem to be an option since they are by design members of an instance.

I've considered defining them all in the parent and looking up the instance type to decide whether or not it becomes available to instantiated children. Such as:

class Parent(object):
    if isinstance(self, (Child1, Child2)):
            @property
            def interval(self):
                pass

            @interval.setter
            def interval(self, value):
                pass

I suppose I could save the property getter/setter methods as strings in another module and exec them into the functions and define the docstrings there as well.

interval_get = """@property
def interval(self):
    \"\"\"
    The interval at which the test should run.

    Parameters
    ----------
    value : int
        Testing interval, allowed options: [300, 600, 900, 1800, 3600]

    Returns
    -------
    value : int
        Configured test interval

    Raises
    ------
    ValueError
        If value is not one of [300, 600, 900, 1800, 3600]

    TypeError
        If type provided is not of type int
    \"\"\"
    return self._interval

"""

interval_set = """@interval.setter
def interval(self, value):
    print('works')
    self._interval = value

"""

class TestProp(object):
    exec(interval_get)
    exec(interval_set)

I'm looking for the recommended approach though. Also, I'm not interested in supporting python2.x so this code is for python3.x only. I'm fine with solutions specific to python3.7 even.

Upvotes: 2

Views: 293

Answers (2)

progmatico
progmatico

Reputation: 4964

If you were using read-only data I would say use namedtuples. Store the property names in a config file as strings for each type and create the appropriate namedtuples at runtime.

As they look read/write because of the setters, maybe the 3rd-party recordclasses mentioned in this answer will do.

Upvotes: 0

Zak
Zak

Reputation: 25205

It sounds somewhat like you are asking a design question, but design questions are often specific to the problem domain, and you aren't really providing problem domain information. That said, it sounds like you currently have a design based on inheritance. One thing you can consider is a design based on composition. In other words, all these subtypes you have may not be formal types in and of themselves, but could be compositions of fundamental data types. Given the types you mentioned i.e., intervals and durations, it seems likely that your current "parent" type could maintain lists or maps of intervals and durations, and you wouldn't need subclasses at all. Or alternatively, it seems likely that an interval or duration would have some semantic meaning, and there is a type that combines a grouped set of those things in a meaningful way.

In any case, I recommend you add more domain specific information, and possibly examples if you want more specific design help.

Upvotes: 1

Related Questions