如何一般性地将一个函数的覆盖应用于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 

请注意,子类具有与类名完全相同的代码,并执行完全相同的操作。我一直在试图弄清楚什么是最好的方式来通用这个,所以我可以保持它干燥,并很容易地用于其他类,并已考虑装饰器和/或多重继承 - 这两个都是我的新概念 - 但我继续混淆。

帮助表示赞赏!

(作为一个方面说明,随意指出我的Django的代码:)你看任何问题)

+0

问题应该可能要求解决问题的方法,因此它不涉及在表单中嵌入大量隐藏字段? – 2011-05-19 01:55:18

+0

谢谢你的建议,约翰。发布了一个单独的问题,描述了我正在使用的场景。 http://*.com/questions/6054124。如果您有一些想法,请随时发布答案。我很想弄清楚如何以更清洁的方式实现这一点。 – 2011-05-19 05:23:09

+0

+1用于查找我答案中的错误;现在我更了解python了! – SingleNegationElimination 2011-05-20 00:59:49

一个选项是使用Mixin类;例如:

首先,共同的行为去的混入:

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真正的优势,你应该阅读Python 2.3和更高版本中的C3 MRO algorithm

基类和以前一样,但由于大家的代码来自混入,派生类就变得琐碎

class HiddenSlideForm(SomeMixin, SlideForm): 
    pass 

class HiddenDeckForm(SomeMixin, DeckForm): 
    pass 

注意,混合类出现第一,因为我们无法控制*Form类在其init方法中执行。

如果__init__的方法都不平凡,您仍然会获得胜利。

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

确保object是在继承图,某处。奇怪的事情可能会发生,否则。

+2

感谢您的回复!我从Django提供的基类继承(ModelForm),但是你的代码对我不起作用。 Mixin中的init函数永远不会被调用。以下是一个简单的测试案例,演示发生了什么:http://pastebin.com/a1tnXsjA。任何想法是什么修复? (顺便说一句,我在Python 2.6) – 2011-05-19 07:17:22

+0

而不是使用超级,只需在'HiddenSlideForm .__ init __()'中明确(一个接一个地)调用'SomeMixin'和'SlideForm'的'__init __()'。 – Imran 2011-05-20 07:11:54

+0

Imram,尽管这并不理想,因为我将不得不在每个子类中编写一个init,但它看起来可能是实现它的方法,因为其他建议的用于使用Mixin自动执行覆盖的想法不会似乎工作。如果您创建了评论的答案形式版本,我会将其标记为已接受,因为它实际上可行。 – 2011-05-23 14:28:18

多重继承(具体Mixins)很可能是这里的最佳解决方案。

例子:

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) 
+2

嗯...不完全。在这种情况下,您需要调用'super',特别是在用于覆盖特殊方法的mixin类时。 – SingleNegationElimination 2011-05-19 01:46:53