Are Mixin class __init__ functions not automatically called? Are Mixin class __init__ functions not automatically called? python python

Are Mixin class __init__ functions not automatically called?


Sorry I saw this so late, but

class MixedClass2(SomeMixin, MyClass):    pass>>> m = MixedClass2() mixin before  base  mixin after

The pattern @Ignacio is talking about is called cooperative multiple inheritance, and it's great. But if a base class isn't interested in cooperating, make it the second base, and your mixin the first. The mixin's __init__() (and anything else it defines) will be checked before the base class, following Python's MRO.

This should solve the general question, though I'm not sure it handles your specific use. Base classes with custom metaclasses (like Django models) or with strange decorators (like @martineau's answer ;) can do crazy things.


Have the base class invoke super().__init__() even though it is a subclass of object. That way all the __init__() methods will be run.

class BaseClassOne(object):    def __init__(self, *args, **kwargs):        super(BaseClassOne, self).__init__(*args, **kwargs)        print (" base ")


Python performs no implicit calls to the __init__ methods of a class' super-class(es)—but it's possible to make it happen automatically. One way is by defining a metaclass for your mixed class(es) that creates or extends the mixed class' __init__ method so that it calls all the listed bases' __init__ functions in the order they were listed.

A second way is to do it is to use a class decorator—which is shown in the Edit section below.

Using a metaclass:

class APIBaseClassOne(object):  # API class (can't be changed)    def __init__(self, *args, **kwargs):        print('  APIBaseClassOne.__init__()')class SomeMixin(object):    def __init__(self, *args, **kwargs):        print('  SomeMixin.__init__()')class MixedClassMeta(type):    def __new__(cls, name, bases, classdict):        classinit = classdict.get('__init__')  # Possibly None.        # Define an __init__ function for the new class.        def __init__(self, *args, **kwargs):            # Call the __init__ functions of all the bases.            for base in type(self).__bases__:                base.__init__(self, *args, **kwargs)            # Also call any __init__ function that was in the new class.            if classinit:                classinit(self, *args, **kwargs)        # Add the local function to the new class.        classdict['__init__'] = __init__        return type.__new__(cls, name, bases, classdict)class MixedClass(APIBaseClassOne, SomeMixin):    __metaclass__ = MixedClassMeta  # important    # If exists, called after the __init__'s of all the direct bases.    def __init__(self, *args, **kwargs):        print('  MixedClass.__init__()')print('MixedClass():')MixedClass()

Output:

MixedClass():  APIBaseClassOne.__init__()  SomeMixin.__init__()  MixedClass.__init__()

Edit

Here's how to accomplish the same thing with a class decorator (requires Python 2.6+):

class APIBaseClassOne(object):  # API class (can't be changed)    def __init__(self, *args, **kwargs):        print('  APIBaseClassOne.__init__()')class SomeMixin(object):    def __init__(self, *args, **kwargs):        print('  SomeMixin.__init__()')def mixedomatic(cls):    """ Mixed-in class decorator. """    classinit = cls.__dict__.get('__init__')  # Possibly None.    # Define an __init__ function for the class.    def __init__(self, *args, **kwargs):        # Call the __init__ functions of all the bases.        for base in cls.__bases__:            base.__init__(self, *args, **kwargs)        # Also call any __init__ function that was in the class.        if classinit:            classinit(self, *args, **kwargs)    # Make the local function the class's __init__.    setattr(cls, '__init__', __init__)    return cls@mixedomaticclass MixedClass(APIBaseClassOne, SomeMixin):    # If exists, called after the __init__'s of all the direct base classes.    def __init__(self, *args, **kwargs):        print('  MixedClass.__init__()')print('MixedClass():')MixedClass()

Notes

For Python < 2.6, use MixedClass = mixedomatic(MixedClass) following the class definition.

In Python 3 the syntax for specifying metaclasses is different, so instead of the:

class MixedClass(APIBaseClassOne, SomeMixin):    __metaclass__ = MixedClassMeta  # important

shown above, you would need to use:

class MixedClass(APIBaseClassOne, SomeMixin, metaclass=MixedClassMeta):

The class decorator version will work as-is in both versions.