关于django:如何在python中将一个函数的重写一般应用于多个类?

How to generically apply an override of a function to mutiple classes in python?

我正在开发一个django应用程序,但这似乎只是一个python问题,没有任何特定于django的问题。我对python还很陌生,很难描述我想做什么,但更容易展示,下面是:

我有一个班:

1
2
3
4
class SlideForm(ModelForm):

    class Meta:
        model = Slide

我将其分为:

1
2
3
4
5
6
class HiddenSlideForm(SlideForm):
    def __init__(self, *args, **kwargs):
        super(HiddenSlideForm, self).__init__(*args, **kwargs)
        for name, field in self.fields.iteritems():
            field.widget = field.hidden_widget()
            field.required = False

然后我又上了一节课:

1
2
3
4
5
6
7
8
9
class DeckForm(ModelForm):    

    def __init__(self, *args, **kwargs):
        # do some stuff here
        return super(DeckForm, self).__init__(*args, **kwargs)

    class Meta:
        model = Deck
        # other stuff here

我也分为以下几类:

1
2
3
4
5
6
7
class HiddenDeckForm(DeckForm):

    def __init__(self, *args, **kwargs):
        super(HiddenDeckForm, self).__init__(*args, **kwargs)
        for name, field in self.fields.iteritems():
            field.widget = field.hidden_widget()
            field.required = False

请注意,子类除了类名外,还有完全相同的代码,并且执行完全相同的操作。我一直在尝试找出什么是最好的方法来将它遗传,这样我就可以让它保持干燥,并且可以很容易地用于其他类,并且考虑了装饰和/或多重继承——这两个都是我的新概念——但是我总是搞混了。

感谢您的帮助!

(作为旁注,请随时指出您在我的Django代码中看到的任何问题:)


一个选项是使用mixin类;示例:

首先,在mixin中常见的行为是:

1
2
3
4
5
6
class SomeMixin(object):
    def __init__(self, *args, **kwargs):
        super(SomeMixin, self).__init__(*args, **kwargs)
        for name, field in self.fields.iteritems():
            field.widget = field.hidden_widget()
            field.required = False

在某种程度上,您可以合理地控制继承图中的所有类,只要您在需要重写的每个方法中调用super,那么派生类的外观就不重要了。

然而,当其中一个超类本身在正确的时间不称为super时,就会遇到问题。在这种情况下,必须最后调用重写的方法,这非常重要,因为一旦调用它,就不会再进行调用。

最简单的解决方案是确保每个类实际上是从有问题的超类派生的,但在某些情况下,这是不可能的;派生一个新类会创建一个您实际上不想存在的新对象!另一个原因可能是因为逻辑基类在继承树上太高,无法计算出来。

在这种情况下,您需要特别注意基类的列出顺序。除非继承关系图中存在更派生的类,否则python将首先考虑最左边的超类。这是一个涉及到的主题,为了理解Python真正在做什么,您应该阅读一下Python2.3和更高版本中的c3 mro算法。

基类和以前一样,但是由于所有公共代码都来自mixin,派生类变得微不足道。

1
2
3
4
5
class HiddenSlideForm(SomeMixin, SlideForm):
    pass

class HiddenDeckForm(SomeMixin, DeckForm):
    pass

注意,mixin类首先出现,因为我们无法控制*Form类在其init方法中的作用。

如果任何一种方法的__init__方法都是不平凡的,那么您仍然会获得胜利。

1
2
3
4
class HiddenSlideForm(SomeMixin, SlideForm):
    def __init__(self, *args, **kwargs):
        super(HiddenSlideForm, self).__init__(*args, **kwargs)
        do_something_special()

确保object在继承图的某个地方。否则会发生奇怪的事情。


多重继承(特别是mixin)可能是这里最好的解决方案。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class HiddenFormMixin(object):
    def __init__(self, *args, **kwargs):
        for name, field in self.fields.iteritems():
            field.widget = field.hidden_widget()
            field.required = False


class SlideForm(ModelForm):
    class Meta:
        model = Slide


class HiddenSlideForm(SlideForm, HiddenFormMixin):
    pass


class DeckForm(ModelForm):    
    def __init__(self, *args, **kwargs):
        # do some stuff here
        return super(DeckForm, self).__init__(*args, **kwargs)

    class Meta:
        model = Deck
        # other stuff here  


class HiddenDeckForm(DeckForm, HiddenFormMixin):
    pass

请注意,如果在这两个类中都覆盖__init__,这可能不会直接起作用。在这种情况下,您可以这样做来指定顺序:

1
2
3
4
5
class HiddenDeckForm(DeckForm, HiddenFormMixin):
    def __init__(self, *args, **kwargs):
        # do some stuff here
        DeckForm.__init__(self, *args, **kwargs)
        HiddenFormMixin.__init__(self, *args, **kwargs)