Question

I have a class, which is supposed to contain several other classes as its attributes - I believe composition would be the right term. Now, I want to initialize the attributes of the contained classes depending on whether they have been set in the containing class. In the following example, I would just override the defaults of the two minor classes with None, which I do not want:

class OneAttribute:
    def __init__(self,a=1):
        self.a = a

class TwoAttributes:
    def __init__(self,b=2,c=3):
        self.b = b
        self.c = 3

class SuperClass:
    def __init__(self,a=None,b=None,c=None):
        '''
        This would override the defaults in the
        other two classes, which is bad.
        '''
        self.one = OneAttribute(a=a)
        self.two = TwoAttributes(b=b,c=c)

I could alternatively define the containing class like below, but this is not very nice nor extensible simply horrible:

Class SuperClass:
    def __init__(self,a=None,b=None,c=None):
        if a is not None:
            self.one = OneAttribute(a=a)
        else:
            self.one = OneAttribute()

        if b is not None and c is not None:
            self.two = TwoAttributes(b=b, c=c)
        elif b is not None:
            self.two = TwoAttributes(b=b)
        elif c is not None:
            self.two = TwoAttributes(c=c)
        else:
            self.two = TwoAttributes()

Is what I am trying to achieve maybe a bad design decision?

Was it helpful?

Solution

One solution is to allow your class to accept extra keyword parameters

class OneAttribute:
    def __init__(self,a=1,**kwargs):
        self.a = a

class TwoAttributes:
    def __init__(self,b=2,c=3,**kwargs):
        self.b = b
        self.c = c

Then you can simply accept keyword parameters in the container class

class SuperClass:
    def __init__(self, **kwargs):
        self.one = OneAttribute(**kwargs)
        self.two = TwoAttributes(**kwargs)

The downside is that if you pass an extra parameter to the two inner classes in other it would go undetected.

Avoiding this problem is possible, but requires some black magic: first you just declare the inner classes as usual:

class OneAttribute:
    def __init__(self,a=1):
        self.a = a

class TwoAttributes:
    def __init__(self,b=2,c=3):
        self.b = b
        self.c = c

then you discover using introspection what are the parameters they expect

def parms_of(f):
    return f.__code__.co_varnames[:f.__code__.co_argcount]

and pass only those to the inner classes constructor

def filter_kwargs(kwargs, f):
    s = set(parms_of(f))
    return dict((k, v) for k, v in kwargs.items()
                if k in s)

class SuperClass:
    def __init__(self, **kwargs):
        self.one = OneAttribute(**filter_kwargs(kwargs, OneAttribute.__init__))
        self.two = TwoAttributes(**filter_kwargs(kwargs, TwoAttributes.__init__))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top