Xabs
Xabs

Reputation: 4664

Cooperative multiple inheritance and python 2.7 mixins

I'm trying to use the cooperative multiple inheritance pattern to resolve a problem. A very simplified version of my python 2.7 code looks like this:

class Base1(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def to_tuple(self):
        return self.a, self.b

    def to_string(self):
        return '%s.%s' % self.to_tuple()   # (1)

class Base2(object):

    def __init__(self, c, d):
        self.c = c
        self.d = d

    def to_tuple(self):
        return self.c, self.d

    def to_string(self):
        return '%s-%s' % self.to_tuple()    #(2)

class MyMixin(Base1, Base2):

    def __init__(self, a, b, c, d):
        Base1.__init__(self, a, b)
        Base2.__init__(self, c, d)

    def to_tuple(self):
        return Base1.to_tuple(self) + Base2.to_tuple(self)

    def to_string(self):
        return '{}: {} '.format(Base1.to_string(self), Base2.to_string(self))


mix = MyMixin('a', 'b', 'c', 'd')
print(mix.to_string())

After writing this code, I was expecting the result:

a.b: c-d 

but the code fails. When the line #(1) is run, self is a MyMixin class, not a Base1 class, so to_tuple returns 4 items.

The only way I've found to fix this is to replace the lines #(1) and #(2) above with:

return '%s.%s' % Base1.to_tuple()    # (1)
return '%s.%s' % Base2.to_tuple()    # (2)

and this feels terribly wrong for a number of reasons.

What am I doing wrong?

Upvotes: 1

Views: 232

Answers (1)

taras
taras

Reputation: 3705

Here is what happens. When mix.to_string() is called, first, it calls Base1.to_string(self) passing a mix instance as a self, which means when to_string is called on Base1 it has an instance of MyMixin which returns ('a','b','c','d') on to_tuple call. That's why it fails, cuz tuple contains 4 items and only 2 are required by line #1.

To solve this issue try to avoid inheritance from multiple classes with the same method signatures. Use composition instead.

class MyMixin(object):

    def __init__(self, a, b, c, d):
        self.base1 = Base1(a, b)
        self.base2 = Base2(c, d)

    def to_tuple(self):
        return self.base1.to_tuple(self) + self.base2.to_tuple(self)

    def to_string(self):
        return '{}: {} '.format(self.base1.to_string(), self.base2.to_string())

Upvotes: 2

Related Questions