Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/344.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Mixin类的init函数是否没有自动调用?_Python_Inheritance_Multiple Inheritance_Mixins - Fatal编程技术网

Python Mixin类的init函数是否没有自动调用?

Python Mixin类的init函数是否没有自动调用?,python,inheritance,multiple-inheritance,mixins,Python,Inheritance,Multiple Inheritance,Mixins,我希望使用Mixin始终向子类添加一些init功能,每个子类继承自不同的API基类。具体地说,我想创建多个不同的子类,这些子类继承自这些不同API提供的基类之一和一个Mixin,它们将始终以相同的方式执行Mixin初始化代码,而不进行代码复制。然而,似乎Mixin类的uuu init uuu函数永远不会被调用,除非我在子类的uuu init uu函数中显式调用它,这并不理想。我构建了一个简单的测试用例: class APIBaseClassOne(object): def __init_

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

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,并以这种方式保持其干燥。但是,它们继承自具有共同功能的不同基类。(精确地说,Django字段类)。

让基类调用
super()。\uuu init\uuuu()
,即使它是
对象的子类。这样所有的
\uuuu init\uuuu()
方法都将运行

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

Python不执行对类的超类(es)的
\uuuuu init\uuuu
方法的隐式调用,但可以自动执行。一种方法是为您的混合类定义一个元类,该元类创建或扩展混合类的“
\uuuu init\uuu
方法,以便它按照列出的顺序调用所有列出的基函数”

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

使用元类:

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.\uuuuu init\uuuuuuuu()
SomeMixin.\uuuuu init\uuuuuuu()
MixedClass.\uuuu init\uuuuuu()
编辑

下面介绍如何使用类装饰器(需要Python 2.6+)完成同样的任务:

注释

对于Python<2.6,在类定义之后使用
MixedClass=mixedomatic(MixedClass)

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

class MixedClass(APIBaseClassOne, SomeMixin):
    __metaclass__ = MixedClassMeta  # important
如上所示,您需要使用:

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

类装饰器版本将在两个版本中都能正常工作。

很抱歉这么晚才看到这个,但是

class MixedClass2(SomeMixin, MyClass):
    pass

>>> m = MixedClass2()
 mixin before 
 base 
 mixin after
@Ignacio所讨论的模式被称为合作多重继承,这很好。但是如果一个基类对协作不感兴趣,那么就把它作为第二个基类,把你的mixin作为第一个基类。mixin的
\uuuu init\uuuu()
(以及它定义的任何其他内容)将在基类之前检查,然后检查Python的


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

谢谢!我没有意识到调用super会导致运行所有的uuu init_uuu()方法。不幸的是,BaseClassOne是一个API(Django)提供的基类(我更新了我的问题以反映这一点),但这可能会让我朝着正确的方向开始。它不会导致所有的基类都运行,但会导致下一个基类运行,即使是在混合中。每天学习新的东西…;-)谢谢。@kindall:我想很多人都忽略了OP不能更改
BaseClassOne
这一点,因为它是他们无法控制的API的一部分。@martineau:这个事实是在我发布了这个答案之后才被揭示出来的。这并不意味着这个答案是不正确的,只是与实际情况的相关性要小得多。一般来说,对不是为多重继承而设计的基类使用多重继承是个坏主意。混合类通常是一起设计的,而任意类的混合会产生这样的混乱。在任何情况下,如果两个基类都有一个 IythiNITSy<<代码>方法,那么解释器应该知道该调用哪一个,或者调用它们的顺序是什么?虽然另一个答案中的讨论让我重新思考这是否是一个好主意,但这个实现是可行的,我学到了很多。@Ben:是的,混音有点争议,甚至有些人认为它是有害的,在Python中使用它们的做法有点复杂,因为它默认情况下不调用基类构造函数(不像,C++,会)。然而,在您无法修改API类的情况下,它们的使用可能是合理的,但是您可能需要考虑更多的替代方法,以完成您需要的工作,例如子类化或包装API类。另一种可能是将它的一个实例作为您自己的一个类的属性。不确定,我希望两个都能接受,但您的已被重新接受:)@Ben:谢谢。我可以理解你的困境,但我的[有偏见的]观点是,我的观点更好,因为它不仅实际解决了你的特定问题,而且以一种通用的方式实现了这一点——因此比只调用基类的
init()
要复杂一些。现实世界的问题可能就是这样。我真的很喜欢混合式装饰。我稍微修改了它:
classinit=cls.\uuuu init\uuuuuuuu如果在cls中使用'\uuuu init\uuuuuuuuu'.\uuuu dict\uuuuuuuuuu'.\uuuuu其他都没有
即使时间晚了,这也可以很好地归结为一点(尽管其他答案提供了一些非常好的指导)——最终,在开始任何多重继承冒险之前,对Python MRO的理解似乎至关重要,否则会发生意想不到的事情。谢谢欢迎完全正确,不幸的是,MRO作为一个高级主题出现:/
class MixedClass(APIBaseClassOne, SomeMixin, metaclass=MixedClassMeta):
class MixedClass2(SomeMixin, MyClass):
    pass

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