关于python:mixin类的__init__函数不是自动调用的吗?

Are Mixin class __init__ functions not automatically called?

我希望使用mixin始终向我的子类添加一些init功能,每个子类都继承自不同的API基类。具体地说,我想创建多个不同的子类,这些子类继承自这些不同的API提供的一个基类和一个mixin,它总是以相同的方式执行mixin初始化代码,而不进行代码复制。然而,似乎mixin类的uu in it_uuu函数永远不会被调用,除非我在子类的uu in it_uuu函数中显式调用它,这不太理想。我建立了一个简单的测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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:

1
2
3
4
>>> import test
>>> test.MixedClass()
 base
<test.MixedClass object at 0x1004cc850>

有没有一种方法可以做到这一点(调用mixin get时有一个init函数),而不需要编写每个子类来显式调用mixin的init函数?(也就是说,不必在每门课上做类似的事情。)

1
2
3
4
class MixedClass(MyClass, SomeMixin):
    def __init__(*args, **kwargs):
        SomeMixin.__init__(self, *args, **kwargs)
        MyClass.__init__(self, *args, **kwargs)

顺便说一句,如果我所有的子类都继承自同一个基类,我意识到我可以创建一个继承自基类和mixin的新的中产阶级,并保持它的干燥。但是,它们继承自具有公共功能的不同基类。(准确地说,Django Field类)。


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

1
2
3
4
5
6
7
class MixedClass2(SomeMixin, MyClass):
    pass

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

Ignacio所说的模式叫做合作多重继承,非常好。但是如果一个基类对合作不感兴趣,那么就把它设置为第二个基,并把它与第一个基混合。mixin的__init__()(以及它定义的任何其他内容)将在基类之前,按照python的mro进行检查。

这应该解决一般的问题,尽管我不确定它是否能处理您的特定用途。带有自定义元类(如django模型)或带有奇怪装饰的基类(如@martineau's answer;)可以做一些疯狂的事情。


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

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


python不执行对类的super class(es)的__init__方法的隐式调用,但可以自动执行。一种方法是为您的混合类定义一个元类,该类创建或扩展混合类"EDOCX1"〔3〕方法,以便它按列出的顺序调用所有列出的基"EDOCX1"〔3〕函数。

第二种方法是使用类修饰器,如下面的编辑部分所示。

使用元类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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()

输出:

1
2
3
4
MixedClass():
  APIBaseClassOne.__init__()
  SomeMixin.__init__()
  MixedClass.__init__()

编辑

下面介绍如何使用类修饰器(需要python2.6+)完成相同的工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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<2.6,在类定义之后使用MixedClass = mixedomatic(MixedClass)

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

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

如上图所示,您需要使用:

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

类装饰器版本将像两个版本中一样工作。