【问题标题】:calling super on base class python/django在基类 python/django 上调用 super
【发布时间】:2017-02-17 02:18:01
【问题描述】:

在您将其标记为重复之前,让我声明我了解 super 的工作原理,并且我已阅读以下三个链接:

What does 'super' do in Python?
Understanding Python super() with __init__() methods
http://python-history.blogspot.nl/2010/06/method-resolution-order.html

这就是superbaseclasses 的情况下应该如何工作:

class X(object):
    def __init__(self):
        print "calling init from X"
        super(X, self).__init__()


class Y(object):
    def abc(self):
        print "calling abc from Y"
        super(Y, self).abc()

a = X()
# prints "calling init from X"  (works because object possibly has an __init__ method)

b = Y()
b.abc()
# prints "calling abc from Y" and then
# throws error "'super' object has no attribute 'abc'" (understandable because object doesn't have any method named abc)

问题:django 核心实现中,有几个地方在从object 继承的类上使用super 调用methods(在上面的示例中为Y) .例如:有人能解释一下为什么这段代码有效吗?

from django.core.exceptions import PermissionDenied

class LoginRequiredMixin(object):

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated():
            raise PermissionDenied

        return super(LoginRequiredMixin, self).\
            dispatch(request, *args, **kwards)   # why does this work? 

参考:从这次谈话中复制了这段代码:https://youtu.be/rMn2wC0PuXw?t=403

【问题讨论】:

    标签: python django oop


    【解决方案1】:

    之所以有效,是因为 LoginRequiredMixin 旨在用于多重继承场景。在这种情况下,MRO 将解析为类层次结构中同一级别的对象。 (LoginRequiredMixin 旁边指定的其他类型)

    您可以在下面看到顺序也很重要

    Python 2.7.12 (default, Oct 11 2016, 05:20:59)
    [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>>
    >>>
    >>> class Y(object):
    ...     def abc(self):
    ...         print "calling abc from Y"
    ...         super(Y, self).abc()
    ...
    >>> class Z(object):
    ...     def abc(self):
    ...         print "calling abc from Z"
    ...
    >>> class YZ(Y, Z):  # multiple inheritance
    ...     pass
    ...
    >>> c = YZ()
    >>> c.abc()
    calling abc from Y
    calling abc from Z
    >>>
    >>> class ZY(Z,Y): pass
    ...
    >>> ZY().abc()
    calling abc from Z
    

    ZY 基于 MRO 调用 Z.abc,因此 Y.abc 被忽略

    【讨论】:

    • 我不确定我是否理解。从这个实现,你怎么能说它是在做多重继承呢?如果是这样,它继承自的第二个类是什么?
    • @TheWanderer - 如果你要做类似class YourClass(LoginRequiredMixin, MixinWithDispatch) 的事情,如果MixinWithDispatch 扩展object 并具有dispatch() 方法,那么来自LoginRequiredMixinsuper 调用将解析为MixinWithAbc.dispatch()。如果您要单独使用LoginRequiredMixin,除非您弄乱内置函数,否则它将按预期中断。而且,是的,依赖多重继承和 MRO 也是糟糕的框架设计的标志 - 不像修改内置那样多,但实际上也没有任何理由这样做。
    • @zwer 这是一个很好的回应。谢谢你。你能用这个编辑你的答案,以便我可以将它标记为已解决吗?
    • 好了,附上一个示例,说明如何让您的代码以“相同”的方式工作
    • @The Wanderer - 有趣的是,Django 尽管有它的标语,但它似乎并不适合完美主义者。依赖一种语言的怪癖(幸运的是,MRO 在这一点上几乎是确定性的,这在早期的 Python 时代并非如此)并不是一个好的框架。除了打破相当多的非官方 OOP 良好实践™本身,它还可以向用户灌输不良实践。他们可以很容易地创建一个核心 DjangoMixin 类并让用户扩展它,以便保留明确的“所有权”......唉,他们已经创建了一个相当流行的框架,所以我该判断谁?
    【解决方案2】:

    您是否测试了该演示文稿中的所有代码?上面的代码只有在有人在幕后做一些不应该做的事情(在某些 Python 实现中是严格禁止的)时才可能起作用 - 修改 Python 内置函数。

    你也可以这样做——在你开始执行或构建任何东西之前,这样做:

    import __builtin__
    
    class custom_object(object):
        def abc(self):
            print "calling abc from custom_object"
    
    __builtin__.object = custom_object
    

    然后尝试构建您的 XY 类型,看看效果如何。

    附:只是为了强调一点,这仅用于教育目的 - 不要使用它! 真的没有必要求助于这个,你只会让可能需要的开发人员的生活成为一个活生生的地狱将来解开你的代码。

    更新:

    根据上述 Josh J 的建议,LoginRequiredMixin 可能不打算用作独立类,而是添加到多重继承链中。在这种情况下,实现dispatch() 方法并扩展object 的基类可以粘在LoginRequiredMixin 上。考虑到 Python 是如何进行 MRO 的,LoginRequiredMixin 中的 super() 实际上将引用“粘合”类方法。

    你可以让你的代码表现得像这样:

    class Y(object):
        def abc(self):
            print "calling abc from Y"
            super(Y, self).abc()
    
    class Z(object):
        def abc(self):
            print "calling abc from Z"
    
    class YZ(Y, Z):  # multiple inheritance
        pass
    
    c = YZ()
    c.abc()
    # prints "calling abc from Y" and then
    # prints "calling abc from Z"
    

    这仍然是糟糕的框架设计的一个标志(只要考虑一下基于非常简单的代码我们花了多长时间才找到问题的根源),只是比搞乱内置函数稍微不那么残酷。所以,如果有一天你正在设计你的框架也不要这样做

    【讨论】:

    • 感谢您的回答。你说的有些道理,但我严重怀疑django 是否这样做。如果是这种情况,他们将不得不实现许多这样的abcs ..而且我在django github repo 中找不到类似的东西
    • 你说得对,Django 开发人员毕竟是好孩子。我向任何我可能冒犯的人道歉,暗示他们在搞乱内置函数。演示文稿中的代码要么不起作用,要么演示文稿的人使用了内置函数。
    猜你喜欢
    • 1970-01-01
    • 2020-04-06
    • 2015-09-22
    • 2011-06-15
    • 2017-06-30
    • 2018-05-27
    • 1970-01-01
    • 1970-01-01
    • 2016-10-12
    相关资源
    最近更新 更多