Mixin类__init__函数是不是自动调用的?

时间:2011-05-23 14:57:38

标签: python inheritance multiple-inheritance mixins

我想使用Mixin来为我的子类添加一些init功能,每个子类都继承自不同的API基类。具体来说,我想创建多个不同的子类,这些子类继承自这些不同的API提供的基类之一和一个Mixin,它将始终以相同的方式执行Mixin初始化代码,而无需代码复制。但是,似乎不会调用Mixin类的__init__函数,除非我在Child类的__init__函数中显式调用它,这不是理想的。我建立了一个简单的测试用例:

class APIBaseClassOne(object):
    def __init__(self, *args, **kwargs):
        print (" base ")

class SomeMixin(object):
    def __init__(self, *args, **kwargs):
        print (" mixin before ")
        super(SomeMixin, self).__init__(*args, **kwargs)
        print (" mixin after ")

class MyClass(APIBaseClassOne):
    pass

class MixedClass(MyClass, SomeMixin):
    pass

正如您在以下输出中所看到的,Mixin函数的init永远不会被调用:

>>> import test
>>> test.MixedClass()
 base
<test.MixedClass object at 0x1004cc850>

有没有办法做到这一点(在调用Mixin时有一个init函数)而不编写每个子类来显式调用Mixin的init函数? (即,不必在每个班级做这样的事情:)

class MixedClass(MyClass, SomeMixin):
    def __init__(*args, **kwargs):
        SomeMixin.__init__(self, *args, **kwargs)
        MyClass.__init__(self, *args, **kwargs) 

顺便说一下,如果我所有的子类都继承自同一个基类,我意识到我可以创建一个继承自基类和mixin的新中间类,并保持DRY那种方式。但是,它们从具有共同功能的不同基类继承。 (准确地说是Django Field类。)

3 个答案:

答案 0 :(得分:39)

对不起,我这么晚才看到这个,但是

class MixedClass2(SomeMixin, MyClass):
    pass

>>> m = MixedClass2()
 mixin before 
 base 
 mixin after

@Ignacio所讨论的模式称为协作多重继承,它很棒。但是如果基类对合作不感兴趣,那么将它作为第二个基础,并将其作为第一个基础。 mixin的__init__()(以及它定义的任何其他内容)将在Python的MRO之后在基类之前进行检查。

这应解决一般性问题,但我不确定它是否能够处理您的具体用途。具有自定义元类(如Django模型)或奇怪的装饰器(如@ martineau的答案;)的基类可以做疯狂的事情。

答案 1 :(得分:23)

让基类调用super().__init__(),即使它是object的子类。这样就可以运行所有__init__()方法。

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

答案 2 :(得分:17)

Python不会对类'超级类的__init__方法执行隐式调用 - 但是可以自动执行它。一种方法是为混合类定义一个元类,它创建或扩展混合类'__init__方法,以便按照列出的顺序调用所有列出的基础'__init__函数。

第二种方法是使用类装饰器 - 它显示在下面的编辑部分。

使用元类:

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()

输出:

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

修改

以下是如何使用类装饰器完成相同的操作(需要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

@mixedomatic
class 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()

备注

对于Python&lt; 2.6,使用MixedClass = mixedomatic(MixedClass) 跟随类定义。

在Python 3中,指定元类的语法不同,因此不是:

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

如上所示,您需要使用:

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

类装饰器版本将在两个版本中按原样运行。