我想使用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类。)
答案 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):
类装饰器版本将在两个版本中按原样运行。