__init__没有被双重继承类调用

时间:2016-06-12 06:52:34

标签: python django

我有以下代码:

from form_utils import forms as betterforms
from django.db import models

class FilterForm(betterforms.BetterForm):
    def __init__(self, *args, **kwargs):
        super(FilterForm, self).__init__(*args, **kwargs)
        print('filter form __init__')

    ...

class NewEntityForm(FilterForm, FileFormMixin):
    def __init__(self, *args, **kwargs):
        super(NewEntityForm, self).__init__(*args, **kwargs)
        # super(FileFormMixin, self).__init__() <-- really does not matter
        print('newentityform __init__')

FileForMixin定义如下:

class FileFormMixin(object):
    def __init__(self, *args, **kwargs):
        super(FileFormMixin, self).__init__(*args, **kwargs)
        print('file form mixin __init__')

FileFormMixinhttps://github.com/mbraak/django-file-form提供,goodforms由https://github.com/carljm/django-form-utils提供。

问题是,FileFormMixin的{​​{1}}永远不会被调用。我该如何解决这个问题?我真的需要他们所有人。现在它只执行__init__FilterForm构造函数。

更新

所以,我查看了所有提到的类NewEntityForm,并且他们正在调用super()!

__init__

FileFormMixin

class FileFormMixin(object): def __init__(self, *args, **kwargs): super(FileFormMixin, self).__init__(*args, **kwargs)

BetterForm

更多的是,打印类'mro为@ elwin-arens提出,给出以下输出:

class BetterBaseForm(object): ... def __init__(self, *args, **kwargs): self._fieldsets = deepcopy(self.base_fieldsets) self._row_attrs = deepcopy(self.base_row_attrs) self._fieldset_collection = None super(BetterBaseForm, self).__init__(*args, **kwargs) class BetterForm(with_metaclass(BetterFormMetaclass, BetterBaseForm), forms.Form): __doc__ = BetterBaseForm.__doc__

但FileFormMixin的filter form __init__ NewEntityForm.__mro__ (<class 'myapp.forms.NewEntityForm'>, <class 'myapp.forms.FilterForm'>, <class 'form_utils.forms.BetterForm'>, <class 'django.forms.widgets.NewBase'>, <class 'form_utils.forms.BetterBaseForm'>, <class 'django.forms.forms.Form'>, <class 'django.forms.forms.BaseForm'>, <class 'django_file_form.forms.FileFormMixin'>, <class 'object'>) newsiteform __init__只有在我明确地将其称为@ tom-karzes建议时才会执行

3 个答案:

答案 0 :(得分:4)

据推测,BetterForm并没有打电话给超级,因此连锁店停在那里。

你唯一能做的就是在声明中交换类的顺序:

class NewEntityForm(FileFormMixin, FilterForm):

当然,这可能会影响两个类中定义的任何其他方法。

答案 1 :(得分:4)

<强> TL;博士

在您发布的NewEntityForm MRO中,您会看到课程BaseFormIn the source of django.forms.forms.BaseForm您还可以看到此类不会调用super(BaseForm, self).__init__(),因此负责将链断开到FileFormMixin。

在这种情况下,您可以通过更改NewEntityForm基类的顺序来解决此问题:

class NewEntityForm(FileFormMixin, FilterForm):
    def __init__(self, *args, **kwargs):
        super(NewEntityForm, self).__init__(*args, **kwargs)

<强>解释

您可能会想到的是,FilterFormFileFormMixin类并列作为基类存在。

实际上,从多个类继承会创建一个方法解析顺序(MRO),它是一个按顺序遍历的类列表。此顺序确定super在给定上下文中的含义。

演示的简化示例:

class A(object):
  def __init__(self):
    super(A, self).__init__()
    print('A', A.__mro__)

class B(object):
  def __init__(self):
    super(B, self).__init__()
    print('B', B.__mro__)

class C(B, A):
  def __init__(self):
    super(C, self).__init__()
    print('C', C.__mro__)


c = C() 

super接受一个类并按方法解析顺序向右移动一个。因此super(C, self).__init__()调用B.__init__,这应该是显而易见的,但在此上下文中super(B, self).__init__()调用A.__init__而不是object.__init__,这可能不太明显。

这是因为在上面的代码示例中,C具有以下method resolution order

C (<class 'C'>, <class 'B'>, <class 'A'>, <class 'object'>)

把它放在图片中,左边是你可能期望的,右边是实际发生的事情:

    Mental model             Actual model
        C                        C
        |                        |
       / \                       B
      B   A                      |
      |   |                      A
       \ /                       |   
      object                   object

所以,如果B没有称之为超级:

class A(object):
  def __init__(self):
    super(A, self).__init__()
    print('A')

class B(object):
  def __init__(self):
    print('B')

class C(B, A):
  def __init__(self):
    super(C, self).__init__()
    print('C')


c = C()

这会导致在调用__init__()后未调用A super(C, self).__init__()

这就引出了NewEntityForm的MRO看起来像什么的问题。您可以像这样打印mro:

print('NewEntityForm.__mro__', NewEntityForm.__mro__)

这可能会显示一个列表,其中FileFormMixin来自一个不称之为超级初始化方法的类,正如丹尼尔罗斯曼已经说过的那样。

一种可能的解决方案可能是显式调用要应用的类的init,如下所示:

class NewEntityForm(FilterForm):
    def __init__(self, *args, **kwargs):
        super(NewEntityForm, self).__init__(*args, **kwargs)
        FileFormMixin.__init__(self)
        print('newentityform __init__')

或者你可以翻转NewEntityForm的基类。

答案 2 :(得分:0)

在调用__init__方法时,可以显式引用父类。例如:

class Foo(object):
    def __init__(self, x):
        self.x = x

class Bar(object):
    def __init__(self, y):
        self.y = y

class Baz(Foo, Bar):
    def __init__(self, x, y):
        Foo.__init__(self, x)
        Bar.__init__(self, y)