[Tutor] Multiple inheritance for mixin attributes

Steven D'Aprano steve at pearwood.info
Mon Aug 16 13:01:53 CEST 2010


On Mon, 16 Aug 2010 05:01:58 pm Knacktus wrote:
> Hello everyone,
>
> I've got a question about class design. I want to model classes like
> these (examples):
>
> #####################################################################
> class BaseItem(object):
>      def __init__(self, ident, name, description):
>          self.ident = ident
>          self.name = name
>          self.description = description
>
> class DataSourceItem(object):
>      def __init__(self, ident, name, description, data_source):
>          self.ident = ident
>          self.name = name
>          self.description = description
>          self.data_source = data_source


That's a clear job for single inheritance:

class DataSourceItem(BaseItem):
    def __init__(self, ident, name, description, data_source):
        BaseItem.__init__(self, ident, name, description)
        self.data_source = data_source

Note: there's a BIG question mark as to whether you should use super() 
or not when inheriting methods. I've previously defended the use of 
super() even in single-inheritance classes, where it is not strictly 
necessary but does no harm. However in this case, the function 
signature of DataSourceItem and BaseItem are different, which is a 
clear signal *not* to use super(). But if you're brave, here's a 
version that uses super():

class DataSourceItem(BaseItem):
    def __init__(self, ident, name, description, data_source):
        super(DataSourceItem, self).__init__(ident, name, description)
        self.data_source = data_source

Is this safe? Well, probably, for the simple class hierarchy you enter. 
But if you extend the class hierarchy with more sibling and child 
classes, perhaps not.


> class BaseItemCollection(list):
>      def __init__(self, ident, name, description):
>          self.ident = ident
>          self.name = name
>          self.description = description
>      def default_version(self):
>          return self[-1]


That's a clear job for composition:

class BaseItemCollection(list):
    def __init__(self, ident, name, description):
        self.data = BaseItem(ident, name, description)
    def default_version(self):
        return self[-1]


> class BaseDataSourceItemCollection(list):
>      def __init__(self, ident, name, description, data_source):
>          self.ident = ident
>          self.name = name
>          self.description = description
>          self.data_source = data_source
>      def default_version(self):
>          return self[-1]

This one isn't clear. You could inherit either directly from list:

class BaseDataSourceItemCollection(list):
    def __init__(self, ident, name, description, data_source):
        self.data = DataSourceItem(
            ident, name, description, data_source)
    def default_version(self):
        return self[-1]


or from BaseItemCollection:

class BaseDataSourceItemCollection(BaseItemCollection):
    def __init__(self, ident, name, description, data_source):
        BaseItemCollection.__init__(self, ident, name, description)
        self.data_source = data_source


Which such trivially small classes, there's no real advantage or 
disadvantage to either.

Of course, you could use multiple inheritance, but that opens a whole 
rat's nest of difficulties. Even though Python makes it easy to 
implement multiple-inheritance, it is full of pitfalls for the unwary, 
and even the wary, so you should always consider whether you *really* 
need it.

Other alternatives are traits and generic functions:

http://www.artima.com/weblogs/viewpost.jsp?thread=246488
http://www.artima.com/weblogs/viewpost.jsp?thread=237764


Lastly, I should mention automatic delegation as an alternative to 
inheritance. It's not clear to me that this would be either useful or 
necessary for your classes, but I'll mention it so you can google for 
more information if you are interested.



-- 
Steven D'Aprano


More information about the Tutor mailing list