Переопределение функции множественного наследования Python и ListView в django - PullRequest
17 голосов
/ 30 марта 2012

Я создал класс, который подклассов ListView и два пользовательских миксина, которые реализовали функцию get_context_data. Я хотел переопределить эту функцию в дочернем классе:

from django.views.generic import ListView

class ListSortedMixin(object):
    def get_context_data(self, **kwargs):
        print 'ListSortedMixin'
        return kwargs

class ListPaginatedMixin(object):
    def get_context_data(self, **kwargs):
        print 'ListPaginatedMixin'
        return kwargs

class MyListView(ListSortedMixin, ListPaginatedMixin, ListView):
  def get_context_data(self, **context):
    super(ListSortedMixin,self).get_context_data(**context)
    super(ListPaginatedMixin,self).get_context_data(**context)
    return context

Когда я выполняю MyListView, он печатает только "ListSortedMixin". По некоторым причинам python выполняет ListSortedMixin.get_context_data вместо MyListView.get_context_data. Зачем?

Если я изменю порядок наследования на ListPaginatedMixin, ListSortedMixin, ListView, будет выполнено ListPaginatedMixin.get_context_data.

Как мне переопределить функцию get_context_data?

Ответы [ 2 ]

19 голосов
/ 21 июля 2013

Это старый вопрос, но я считаю, что ответ неверный. В вашем коде есть ошибка. Следует читать:

class MyListView(ListSortedMixin, ListPaginatedMixin, ListView):
    def get_context_data(self, **context):
        super(MyListView,self).get_context_data(**context)
        return context

Порядок, в котором будет вызываться get_context_data, следует тому же порядку, который указан в объявлении MyListView. Обратите внимание, что аргумент супер - MyListView, а не суперклассы.

UPDATE

Я пропустил, что ваши миксины не называют супер. Им следует. Да, даже если они наследуют от объекта, потому что super вызывает следующий метод в MRO, а не обязательно родительский класс, в котором он находится.

from django.views.generic import ListView

class ListSortedMixin(object):
    def get_context_data(self, **kwargs):
        print 'ListSortedMixin'
        return super(ListSortedMixin,self).get_context_data(**context)

class ListPaginatedMixin(object):
    def get_context_data(self, **kwargs):
        print 'ListPaginatedMixin'
        return super(ListPaginatedMixin,self).get_context_data(**context)

class MyListView(ListSortedMixin, ListPaginatedMixin, ListView):
    def get_context_data(self, **context):
        return super(MyListView,self).get_context_data(**context)

Для MyListView тогда MRO равно:

  1. MyListView
  2. ListSortedMixin
  3. ListPaginatedMixin
  4. ListView
  5. Что бы ни было выше ListView ... п. объект

Называть их по одному может сработать, но это не то, как предполагалось.

ОБНОВЛЕНИЕ 2

Скопируйте и вставьте пример, чтобы доказать мою точку зрения.

class Parent(object):
    def get_context_data(self, **kwargs):
        print 'Parent'

class ListSortedMixin(object):
    def get_context_data(self, **kwargs):
        print 'ListSortedMixin'
        return super(ListSortedMixin,self).get_context_data(**kwargs)

class ListPaginatedMixin(object):
    def get_context_data(self, **kwargs):
        print 'ListPaginatedMixin'
        return super(ListPaginatedMixin,self).get_context_data(**kwargs)

class MyListView(ListSortedMixin, ListPaginatedMixin, Parent):
    def get_context_data(self, **kwargs):
        return super(MyListView,self).get_context_data(**kwargs)


m = MyListView()
m.get_context_data(l='l')
10 голосов
/ 30 марта 2012

Если вы пытаетесь вызвать переписанные методы в фиксированном порядке.Используйте этот синтаксис:

class MyListView(ListSortedMixin, ListPaginatedMixin, ListView):
  def get_context_data(self, **context):
    ListSortedMixin.get_context_data(self, **context)
    ListPaginatedMixin.get_context_data(self, **context)
    return context

Super не будет работать в этом случае.См. Руководство по super(type[, object]):

Возвратите прокси-объект, который делегирует вызовы метода родительскому или родственному классу type .Это полезно для доступа к унаследованным методам, которые были переопределены в классе.Порядок поиска такой же, как и в getattr (), за исключением того, что сам тип пропускается.

Существует два типичных варианта использования super.В иерархии классов с одним наследованием super может использоваться для ссылки на родительские классы без явного присвоения им имен, что делает код более понятным.Это использование тесно связано с использованием super в других языках программирования.

Второй вариант использования - поддержка кооперативного множественного наследования в динамической среде выполнения.Этот вариант использования уникален для Python и не встречается в статически скомпилированных языках или языках, которые поддерживают только одиночное наследование.Это позволяет реализовать «алмазные диаграммы», где несколько базовых классов реализуют один и тот же метод.Хороший дизайн требует, чтобы этот метод имел одну и ту же сигнатуру вызова в каждом случае (потому что порядок вызовов определяется во время выполнения, потому что этот порядок адаптируется к изменениям в иерархии классов, и потому что этот порядок может включать в себя классы-братья и сестры, неизвестные до времени выполнения).

Итак, аргумент super - это класс, чей прокси родительского или родного класса вы хотите получить.super(ListSortedMixin,self).get_context_data(**context) не обязательно будет звонить get_context_data из ListSortedMixin.Это зависит от порядка разрешения методов (MRO), который вы можете получить, используя print MyListView.__mro__

Так что super() будет вызывать get_context_data родителя или брата.Порядок выполнения адаптируется к изменениям в иерархии классов, и поскольку этот порядок может включать в себя классы-братья и сестры, которые неизвестны до времени выполнения.

...