django中的Python多重继承函数覆盖和ListView

Posted

技术标签:

【中文标题】django中的Python多重继承函数覆盖和ListView【英文标题】:Python multiple inheritance function overriding and ListView in django 【发布时间】:2012-04-13 22:03:33 【问题描述】:

我创建了一个继承ListView 的类和两个实现get_context_data 函数的自定义mixin。我想在子类上覆盖这个函数:

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 函数?

【问题讨论】:

【参考方案1】:

这是一个老问题,但我认为答案不正确。您的代码中有错误。它应该是:

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

get_context_data 的调用顺序与MyListView 声明中指定的顺序相同。注意 super 的参数是 MyListView 而不是超类。

更新

我错过了你的 mixin 不调用 super。他们应该。是的,即使它们继承自 object,因为 super 调用 MRO 中的 next 方法,不一定是它所在类的父类。

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 是:

    我的列表视图 ListSortedMixin ListPaginatedMixin 列表视图 ListView 上面的内容 ... n.对象

一个一个地调用它们可能会起作用,但这不是它的预期使用方式。

更新 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')

【讨论】:

在这种情况下,super(MyListView,self).get_context_data(**context)ListSortedMixin.get_context_data(self, **context) 相同。我认为the previous answer是正确的:我需要一一调用父母函数。 问题是你的mixin没有调用super。即使 mixins 继承自 object,它们也应该调用 super。 Super 代表 MRO 中的下一个对象(方法解析顺序),这取决于在 MyListView 的声明中指定它们的顺序。我将更新我上面的答案以使其更清楚。 没错。因此,您的示例中调用的方法仅为ListSortedMixin。我需要手动调用所有的父函数。 super 也会在必要时调用兄弟姐妹。将更新后的代码(更新 2)复制并粘贴到您最喜欢的 Python shell 中,并说服自己相信事实。【参考方案2】:

如果您要做的是按固定顺序调用覆盖的方法。使用此语法:

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_dataListSortedMixin。这取决于方法解析顺序 (MRO),您可以使用 print MyListView.__mro__ 获得它

所以super() 将调用父级或兄弟级的get_context_data。执行顺序会适应类层次结构的变化,因为该顺序可以包括在运行之前未知的兄弟类。

【讨论】:

感谢您的快速响应!很好的解释,我现在明白了:)【参考方案3】:

我发现大多数 Python 新手并在这里寻找答案的人将使用 Python 3 而不是 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().get_context_data(**kwargs)

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

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


m = MyListView()
m.get_context_data(l='l')

【讨论】:

以上是关于django中的Python多重继承函数覆盖和ListView的主要内容,如果未能解决你的问题,请参考以下文章

C++ 虚函数表解析

C++ 虚函数表解析

C++ 虚函数表解析

C++ 虚函数表解析

Django:从带有元的抽象类的多重继承

python super()函数的用法与多重继承