双重继承类中__init__方法未被调用

7

I have the following code:

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提供,而betterforms由https://github.com/carljm/django-form-utils提供。

问题是,FileFormMixin__init__从未被调用。我该如何解决?我确实需要全部它们。目前它仅执行FilterFormNewEntityForm构造函数。

更新

所以,我查看了所有提到的类的__init__,它们都在调用super()!

FileFormMixin

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

BetterForm:

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__

更进一步,按照@elwin-arens的建议,将打印类的mro,得到以下输出: 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__ 但是,只有在我明确调用它时,才会执行FileFormMixin的__init__,正如@tom-karzes所建议的那样。
3个回答

7

简而言之:

从你发布的MRO中可以看到NewEntityForm类中有BaseForm类。你可以在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具有以下方法解析顺序
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()

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

这就引出了问题,NewEntityForm的MRO是什么样的。你可以像这样打印mro:

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

这可能会显示一个列表,其中FileFormMixin出现在未调用其super init方法的类之后,就像Daniel Roseman已经说过的那样。

一个可能的解决方案是显式调用您想要应用的类的init方法,例如:

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

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


BaseForm 没有调用它的超类。在您添加的 MRO 的 BaseForm 中,BaseForm 的超类应该触发 FileFormMixin__init__。但是,如果任何一个位于 FileFormMixin 之前的类没有调用 super init,那么链就会断掉。https://github.com/django/django/blob/ec6121693f112ae33b653b4364e812722d2eb567/django/forms/forms.py#L72-98 - Elwin Arens

6

假设BetterForm没有调用super,那么继承关系就会在那里断掉。

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

class NewEntityForm(FileFormMixin, FilterForm):

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


也许可以补充一下,为什么交换顺序以更改MRO时没有其他选择 - 正如指出的那样,这可能会导致其他问题。我理解,这是因为betterforms模块在混合类初始化程序中未调用超级方法不被认为是可更改的(第三方代码),对吗? - Dilettant

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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接