Как в общем случае применить переопределение функции к множественным классам в Python? - PullRequest
5 голосов
/ 19 мая 2011

Я работаю над приложением Django, но похоже, что это всего лишь вопрос по питону, не имеющий ничего общего с 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 :))

Ответы [ 2 ]

4 голосов
/ 19 мая 2011

Один из вариантов - использовать класс Mixin;пример:

Во-первых, в 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 в нужное время.Очень важно, чтобы переопределенный метод, в этом случае, вызывался last , так как после его вызова больше вызовов не будет.

Самое простое решение - убедиться, что каждый класс на самом деле является производным от суперкласса-нарушителя, но в некоторых случаях это просто невозможно;создание нового класса создает новый объект, который вы на самом деле не хотите существовать!Другая причина может заключаться в том, что логический базовый класс находится слишком далеко в дереве наследования, чтобы с ним работать.\

В этом случае вам необходимо обратить особое внимание на порядок , в котором перечислены базовые классы.Python сначала рассмотрит крайний левый суперкласс, если на диаграмме наследования не указан более производный класс.Это сложная тема, и чтобы понять, что на самом деле задумывает python, вы должны прочитать об алгоритме C3 MRO , представленном в python 2.3 и более поздних версиях.

Базовые классы, как и раньше,весь общий код взят из mixin, производные классы становятся тривиальными

class HiddenSlideForm(SomeMixin, SlideForm):
    pass

class HiddenDeckForm(SomeMixin, DeckForm):
    pass

Обратите внимание, что класс mixin появляется first , так как мы не можем контролировать, какие классы *Formсделать в их методах инициализации.

Если методы __init__ любого из них нетривиальны, вы все равно получаете выигрыш.

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

Убедитесь, что object находится на диаграмме наследования где-то,Иначе могут случиться странные вещи.

0 голосов
/ 19 мая 2011

Множественное наследование (в частности, 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)
...