Ben*_*rts 52 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
Run Code Online (Sandbox Code Playgroud)
正如您在以下输出中所看到的,Mixin函数的init永远不会被调用:
>>> import test
>>> test.MixedClass()
base
<test.MixedClass object at 0x1004cc850>
Run Code Online (Sandbox Code Playgroud)
有没有办法做到这一点(在调用Mixin时有一个init函数)而不编写每个子类来显式调用Mixin的init函数?(即,不必在每个班级做这样的事:)
class MixedClass(MyClass, SomeMixin):
def __init__(*args, **kwargs):
SomeMixin.__init__(self, *args, **kwargs)
MyClass.__init__(self, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
顺便说一句,如果我所有的子类都继承自相同的基类,我意识到我可以创建一个继承自基类和mixin的新中间类,并保持干燥.但是,它们从具有共同功能的不同基类继承.(准确地说是Django Field类).
Mat*_*ngo 44
对不起,我这么晚才看到这个,但是
class MixedClass2(SomeMixin, MyClass):
pass
>>> m = MixedClass2()
mixin before
base
mixin after
Run Code Online (Sandbox Code Playgroud)
@Ignacio所讨论的模式称为协同多重继承,它很棒.但是如果基类对合作不感兴趣,那么将它作为第二个基础,并将其作为第一个基础.在__init__()Python的MRO之后,将在基类之前检查mixin (以及它定义的任何其他内容).
这应该解决一般问题,虽然我不确定它是否处理您的具体用途.具有自定义元类(如Django模型)或奇怪的装饰器(如@ martineau的答案;)的基类可以做疯狂的事情.
Ign*_*ams 25
让基类调用,super().__init__()即使它是它的子类object.这样__init__()就可以运行所有方法.
class BaseClassOne(object):
def __init__(self, *args, **kwargs):
super(BaseClassOne, self).__init__(*args, **kwargs)
print (" base ")
Run Code Online (Sandbox Code Playgroud)
mar*_*eau 18
Python不会__init__对类的超类(es)的方法执行隐式调用- 但是可以自动执行它.一种方法是为您的混合类定义一个元类,该元类创建或扩展混合类' __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()
Run Code Online (Sandbox Code Playgroud)
输出:
MixedClass():
APIBaseClassOne.__init__()
SomeMixin.__init__()
MixedClass.__init__()
Run Code Online (Sandbox Code Playgroud)
编辑
以下是如何使用类装饰器完成相同的操作(需要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()
Run Code Online (Sandbox Code Playgroud)
笔记
对于Python <2.6,请使用MixedClass = mixedomatic(MixedClass) 以下类定义.
在Python 3中,指定元类的语法是不同的,因此不是:
class MixedClass(APIBaseClassOne, SomeMixin):
__metaclass__ = MixedClassMeta # important
Run Code Online (Sandbox Code Playgroud)
如上所示,您需要使用:
class MixedClass(APIBaseClassOne, SomeMixin, metaclass=MixedClassMeta):
Run Code Online (Sandbox Code Playgroud)
类装饰器版本将在两个版本中按原样运行.