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的
这应该解决一般的问题,尽管我不确定它是否能处理您的特定用途。带有自定义元类(如django模型)或带有奇怪装饰的基类(如@martineau's answer;)可以做一些疯狂的事情。
让基类调用
1 2 3 4 | class BaseClassOne(object): def __init__(self, *args, **kwargs): super(BaseClassOne, self).__init__(*args, **kwargs) print (" base") |
python不执行对类的super class(es)的
第二种方法是使用类修饰器,如下面的编辑部分所示。
使用元类:
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,在类定义之后使用
在python3中,指定元类的语法是不同的,因此不是:
1 2 | class MixedClass(APIBaseClassOne, SomeMixin): __metaclass__ = MixedClassMeta # important |
如上图所示,您需要使用:
1 | class MixedClass(APIBaseClassOne, SomeMixin, metaclass=MixedClassMeta): |
类装饰器版本将像两个版本中一样工作。