如何在Python中对多个类通用地应用函数重写?
我正在做一个Django应用,但这看起来更像是一个Python的问题,并不特别针对Django。我对Python还很陌生,想要描述我想做的事情有点难,但展示出来就简单多了,所以我来试试:
我有一个类:
class SlideForm(ModelForm):
class Meta:
model = Slide
然后我从这个类继承了一个子类:
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
接着我还有另一个类:
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):
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
注意,这些子类除了类名不同,代码完全一样,功能也完全相同。我一直在想怎么才能把这个过程变得更通用,这样我就可以保持代码的简洁性(DRY原则),并且能方便地用于其他类。我考虑过使用装饰器或者多重继承——这两个概念对我来说都是新的——但我总是搞不清楚。
如果有人能帮忙,我会很感激!
(顺便说一下,欢迎指出我Django代码中的任何问题 :))
2 个回答
多重继承(特别是混入类)可能是这里最好的解决方案。
举个例子:
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__
方法,这可能不会直接奏效。在这种情况下,你可以像这样指定顺序:
class HiddenDeckForm(DeckForm, HiddenFormMixin):
def __init__(self, *args, **kwargs):
# do some stuff here
DeckForm.__init__(self, *args, **kwargs)
HiddenFormMixin.__init__(self, *args, **kwargs)
一种选择是使用混入类(Mixin class);举个例子:
首先,把共同的行为放在混入类里:
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到底在干什么,你应该了解一下 C3 MRO算法,这个算法在Python 2.3及以后的版本中都有。
基类和之前一样,但由于所有的共同代码都来自混入类,派生类变得很简单。
class HiddenSlideForm(SomeMixin, SlideForm):
pass
class HiddenDeckForm(SomeMixin, DeckForm):
pass
注意,混入类出现在 最前面,因为我们无法控制 *Form
类在它们的初始化方法中做了什么。
如果其中任何一个的 __init__
方法比较复杂,你仍然会有收获。
class HiddenSlideForm(SomeMixin, SlideForm):
def __init__(self, *args, **kwargs):
super(HiddenSlideForm, self).__init__(*args, **kwargs)
do_something_special()
确保 object
在继承图中某个地方存在。否则可能会发生奇怪的事情。