pythonmultiple-inheritancecopy-constructorin-class-initialization

Python: multiple inheritance, copy constructor, class initialization and () overloading


I was looking for a way to initialise the derived class using copy constructor and () operator like in C++

class Rectangle {
    int width, height;
  public:
    Rectangle (int,int);
    int area () {return (width*height);}
};

Rectangle::Rectangle (int a, int b) {
  width = a;
  height = b;
}

r = Rectangle(2,3)
s = Rectangle(r) /* <--using copy constructor to initialize*/

and then I was thinking how would I implement this way of intitialisation in case I have a class derived from the other two plus members of its own and came up with the below:

class MyBase1(object):
    def __init__(self, *args, **kwargs):
        self.x = kwargs.get('x')
        self.y = kwargs.get('y')
        print("mybase1 {}".format(kwargs))

    def print_base1(self):
        pass


class MyBase2(object):
    def __init__(self, *args, **kwargs):
        self.s = kwargs.get('s')
        self.p = kwargs.get('p')
        print("mybase2 {}".format(kwargs))

    def print_base2(self):
        pass


class MyChild(MyBase1, MyBase2):

    def __init__(self, **kwargs):
        MyBase1.__init__(self, **kwargs)
        MyBase2.__init__(self, **kwargs)
        self.function_name = kwargs.get('function')


    def __call__(self, my_base1, my_base2, **kwargs):
        initialization_dictionary = dict(vars(my_base1), **vars(my_base2))
        initialization_dictionary = dict(initialization_dictionary, **kwargs)
        newInstance = MyChild(**initialization_dictionary)
        return newInstance

calling then:

base1 = MyBase1(x=1, y=2)
base2 = MyBase2(s=3, p=4)

child = MyChild()(base1, base2, function='arcsine') #<--initialising 

[stm for stm in dir(child) if not stm.startswith('__')]
# gives:['function_name', 'p', 'print_base1', 'print_base2', 's', 'x', 'y']

vars(child)
# gives:{'function_name': 'arcsine', 'p': 4, 's': 3, 'x': 1, 'y': 2}

So I was wondering by how much this is non-pythonic way? And if there is a better way (or no-way) to do the same?


Solution

  • Well, you wouldn't want to create an instance to create a new instance so, you probably want a classmethod or staticmethod. This isn't the place for using __call__ either.

    I might do this:

    class MyChild(MyBase1, MyBase2):
        @classmethod
        def build_from_bases(klass, base1, base2, **kwargs):
            kwargs.update(base1.__dict__)
            # Note if base2 has values for x and y, they will clobber what was in base1
            kwargs.update(base2.__dict__)
            return klass(**kwargs)
    

    But using an instance of Base1 and Base2 to build an instance of MyChild doesn't feel like something I'd do in python. Much more likely to use the obvious:

    mychild = MyChild(x=base1.x, y=base1.y, s=base2.s, p=base2.p, function='foo')
    

    Really I'd prefer that, now I don't have to be concerned about clobbering values, or other weirdness.

    You could combine both, if you really want the short cut method:

    class MyChild(MyBase1, MyBase2):
        @classmethod
        def build_from_bases(klass, base1, base2, **kwargs):
           return klass(x=base1.x, y=base1.y, s=base2.s, p=base2.p, **kwargs)
    

    In python less "clever" is frequently "better"