Python 的 super() 如何处理多重继承?

发布于 2024-09-10 13:12:53 字数 485 浏览 4 评论 0原文

super() 如何处理多重继承?例如,给定:

class First(object):
    def __init__(self):
        print "first"

class Second(object):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print "that's it"

super().__init__ 引用了 Third 的哪个父方法?我可以选择运行哪些项目吗?

我知道它与方法解析顺序有关(MRO)。

How does super() work with multiple inheritance? For example, given:

class First(object):
    def __init__(self):
        print "first"

class Second(object):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print "that's it"

Which parent method of Third does super().__init__ refer to? Can I choose which runs?

I know it has something to do with method resolution order (MRO).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

一笔一画续写前缘 2024-09-17 13:12:53

Guido 本人在他的博客文章 方法解析顺序(包括之前的两次尝试)。

在您的示例中,Third() 将调用 First.__init__。 Python 在类的父类中查找从左到右列出的每个属性。在本例中,我们正在寻找__init__。因此,如果您定义

class Third(First, Second):
    ...

Python 将首先查看 First,并且,如果 First 没有该属性,那么它将查看 Second >。

当继承开始交叉路径时(例如,如果 First 继承自 Second),这种情况会变得更加复杂。阅读上面的链接了解更多详细信息,但是,简而言之,Python 将尝试维护每个类在继承列表中出现的顺序,从子类本身开始。

因此,举例来说,如果您有:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First):
    def __init__(self):
        print "third"

class Fourth(Second, Third):
    def __init__(self):
        super(Fourth, self).__init__()
        print "that's it"

MRO 将是 [Fourth, Second, Third, First]。

顺便说一句:如果 Python 找不到一致的方法解析顺序,它将引发异常,而不是退回到可能让用户感到惊讶的行为。

不明确的 MRO 示例:

class First(object):
    def __init__(self):
        print "first"
        
class Second(First):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        print "third"

Third 的 MRO 应该是 [First, Second] 还是 [Second, First]?没有明显的期望,Python 会抛出一个错误:

TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution order (MRO) for bases Second, First

为什么上面的例子缺少 super() 调用?这些示例的目的是展示 MRO 是如何构建的。它们旨在打印“first\nsecond\third”或其他内容。当然,您可以并且应该尝试该示例,添加 super() 调用,看看会发生什么,并更深入地了解 Python 的继承模型。但我的目标是保持简单并展示 MRO 的构建方式。它是按照我解释的方式构建的:

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
 <class '__main__.Second'>, <class '__main__.Third'>,
 <class '__main__.First'>,
 <type 'object'>)

This is detailed with a reasonable amount of detail by Guido himself in his blog post Method Resolution Order (including two earlier attempts).

In your example, Third() will call First.__init__. Python looks for each attribute in the class's parents as they are listed left to right. In this case, we are looking for __init__. So, if you define

class Third(First, Second):
    ...

Python will start by looking at First, and, if First doesn't have the attribute, then it will look at Second.

This situation becomes more complex when inheritance starts crossing paths (for example if First inherited from Second). Read the link above for more details, but, in a nutshell, Python will try to maintain the order in which each class appears on the inheritance list, starting with the child class itself.

So, for instance, if you had:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First):
    def __init__(self):
        print "third"

class Fourth(Second, Third):
    def __init__(self):
        super(Fourth, self).__init__()
        print "that's it"

the MRO would be [Fourth, Second, Third, First].

By the way: if Python cannot find a coherent method resolution order, it'll raise an exception, instead of falling back to behavior which might surprise the user.

Example of an ambiguous MRO:

class First(object):
    def __init__(self):
        print "first"
        
class Second(First):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        print "third"

Should Third's MRO be [First, Second] or [Second, First]? There's no obvious expectation, and Python will raise an error:

TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution order (MRO) for bases Second, First

Why do the examples above lack super() calls? The point of the examples is to show how the MRO is constructed. They are not intended to print "first\nsecond\third" or whatever. You can – and should, of course, play around with the example, add super() calls, see what happens, and gain a deeper understanding of Python's inheritance model. But my goal here is to keep it simple and show how the MRO is built. And it is built as I explained:

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
 <class '__main__.Second'>, <class '__main__.Third'>,
 <class '__main__.First'>,
 <type 'object'>)
思念满溢 2024-09-17 13:12:53

您的代码和其他答案都有错误。他们缺少前两个类中协作子类化工作所需的 super() 调用。更好的是:

class First(object):
    def __init__(self):
        super(First, self).__init__()
        print("first")

class Second(object):
    def __init__(self):
        super(Second, self).__init__()
        print("second")

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print("third")

输出:

>>> Third()
second
first
third

super() 调用在每个步骤中查找 MRO 中的下一个方法,这就是为什么 FirstSecond 必须也有它,否则执行将在 Second.__init__() 末尾停止。


如果没有FirstSecond 中调用 super(),输出将缺少 second >:

>>> Third()
first
third

Your code, and the other answers, are all buggy. They are missing the super() calls in the first two classes that are required for co-operative subclassing to work. Better is:

class First(object):
    def __init__(self):
        super(First, self).__init__()
        print("first")

class Second(object):
    def __init__(self):
        super(Second, self).__init__()
        print("second")

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print("third")

Output:

>>> Third()
second
first
third

The super() call finds the next method in the MRO at each step, which is why First and Second have to have it too, otherwise execution stops at the end of Second.__init__().


Without the super() calls in First and Second, the output is missing second:

>>> Third()
first
third
心安伴我暖 2024-09-17 13:12:53

我想详细阐述一下the answer by lifeless,因为当我开始阅读如何在Python的多重继承层次结构中使用super(),我没有立即明白。

你需要理解的是,super(MyClass, self).__init__()根据使用的方法解析顺序提供了next__init__方法(MRO) 算法在完整继承层次结构的上下文中

最后一部分对于理解至关重要。让我们再次考虑这个示例:

#!/usr/bin/env python2

class First(object):
  def __init__(self):
    print "First(): entering"
    super(First, self).__init__()
    print "First(): exiting"

class Second(object):
  def __init__(self):
    print "Second(): entering"
    super(Second, self).__init__()
    print "Second(): exiting"

class Third(First, Second):
  def __init__(self):
    print "Third(): entering"
    super(Third, self).__init__()
    print "Third(): exiting"

根据 Guido van Rossum 撰写的这篇有关方法解析顺序的文章,解析 __init__ 的顺序是使用“深度优先从左到右遍历”计算的(在 Python 2.3 之前) " :

Third --> First --> object --> Second --> object

删除除最后一个之外的所有重复项后,我们得到:

Third --> First --> Second --> object

那么,让我们看看当我们实例化 Third 类的实例时会发生什么,例如 x = Third().

  1. 根据 MRO Third.__init__ 执行。
    • 打印第三():输入
    • 然后 super(Third, self).__init__() 执行,MRO 返回被调用的 First.__init__
  2. First.__init__ 执行。
    • 打印 First(): 输入
    • 然后 super(First, self).__init__() 执行,MRO 返回被调用的 Second.__init__
  3. Second.__init__ 执行。
    • 打印Second():输入
    • 然后 super(Second, self).__init__() 执行,MRO 返回被调用的 object.__init__
  4. object.__init__ 执行(代码中没有打印语句)
  5. 执行返回到 Second.__init__ ,然后打印 Second(): exiting
  6. 执行返回到 First.__init__ ,然后打印 First(): exiting
  7. 执行返回到 Third.__init__ ,然后打印 Third( ):退出

这详细说明了为什么实例化 Third() 会导致:

Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting

MRO 算法从 Python 2.3 开始得到改进,可以在复杂的情况下很好地工作,但我猜想使用“深度优先从左到右”右遍历”+“删除最后一个重复项”在大多数情况下仍然有效(如果不是这种情况,请发表评论)。请务必阅读 Guido 的博客文章!

I wanted to elaborate the answer by lifeless a bit because when I started reading about how to use super() in a multiple inheritance hierarchy in Python, I did't get it immediately.

What you need to understand is that super(MyClass, self).__init__() provides the next __init__ method according to the used Method Resolution Ordering (MRO) algorithm in the context of the complete inheritance hierarchy.

This last part is crucial to understand. Let's consider the example again:

#!/usr/bin/env python2

class First(object):
  def __init__(self):
    print "First(): entering"
    super(First, self).__init__()
    print "First(): exiting"

class Second(object):
  def __init__(self):
    print "Second(): entering"
    super(Second, self).__init__()
    print "Second(): exiting"

class Third(First, Second):
  def __init__(self):
    print "Third(): entering"
    super(Third, self).__init__()
    print "Third(): exiting"

According to this article about Method Resolution Order by Guido van Rossum, the order to resolve __init__ is calculated (before Python 2.3) using a "depth-first left-to-right traversal" :

Third --> First --> object --> Second --> object

After removing all duplicates, except for the last one, we get :

Third --> First --> Second --> object

So, lets follow what happens when we instantiate an instance of the Third class, e.g. x = Third().

  1. According to MRO Third.__init__ executes.
    • prints Third(): entering
    • then super(Third, self).__init__() executes and MRO returns First.__init__ which is called.
  2. First.__init__ executes.
    • prints First(): entering
    • then super(First, self).__init__() executes and MRO returns Second.__init__ which is called.
  3. Second.__init__ executes.
    • prints Second(): entering
    • then super(Second, self).__init__() executes and MRO returns object.__init__ which is called.
  4. object.__init__ executes (no print statements in the code there)
  5. execution goes back to Second.__init__ which then prints Second(): exiting
  6. execution goes back to First.__init__ which then prints First(): exiting
  7. execution goes back to Third.__init__ which then prints Third(): exiting

This details out why instantiating Third() results in to :

Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting

The MRO algorithm has been improved from Python 2.3 onwards to work well in complex cases, but I guess that using the "depth-first left-to-right traversal" + "removing duplicates expect for the last" still works in most cases (please comment if this is not the case). Be sure to read the blog post by Guido!

撕心裂肺的伤痛 2024-09-17 13:12:53

总体而言

,假设一切都源自 object(如果不是,则只能靠自己了),Python 会根据类继承树计算方法解析顺序 (MRO)。 MRO 满足 3 个属性:

  • 类的子级在其父级之前
  • 左父级在右父级之前
  • 类在 MRO 中仅出现一次

如果不存在这样的顺序,则 Python 会出错。其内部工作原理是类祖先的 C3 线性化。在这里阅读所有相关内容: https://www.python.org/download/releases/ 2.3/mro/

当一个方法被调用时,MRO 中第一次出现的方法就是被调用的方法。任何未实现该方法的类都会被跳过。该方法中对该方法的任何调用都会调用 MRO 中该方法的下一个出现位置。因此,在继承中放置类的顺序以及在方法中对 super 的调用都很重要。

请注意,您可以使用 __mro__ 方法在 python 中查看 MRO。

示例

以下所有示例都具有类的钻石继承,如下所示:

    Parent
    /   \
   /     \
Left    Right
   \     /
    \   /
    Child

MRO 为:

  1. Child
  2. Left
  3. Right
  4. Parent

您可以通过调用 Child.__mro__ 来测试这一点,它返回:

(__main__.Child, __main__.Left, __main__.Right, __main__.Parent, object)

With super 每个方法中的第一个

class Parent(object):
    def __init__(self):
        super(Parent, self).__init__()
        print("parent")

class Left(Parent):
    def __init__(self):
        super(Left, self).__init__()
        print("left")

class Right(Parent):
    def __init__(self):
        super(Right, self).__init__()
        print("right")

class Child(Left, Right):
    def __init__(self):
        super(Child, self).__init__()
        print("child")

Child() 输出:

parent
right
left
child
    

super 每个方法中的最后一个

class Parent(object):
    def __init__(self):
        print("parent")
        super(Parent, self).__init__()

class Left(Parent):
    def __init__(self):
        print("left")
        super(Left, self).__init__()

class Right(Parent):
    def __init__(self):
        print("right")
        super(Right, self).__init__()

class Child(Left, Right):
    def __init__(self):
        print("child")
        super(Child, self).__init__()

Child() 输出:

child
left
right
parent

当并非所有类都调用 super

如果不是继承链中的所有类都调用 super,那么继承顺序最为重要。例如,如果 Left 不调用 super,则永远不会调用 RightParent 上的方法:

class Parent(object):
    def __init__(self):
        print("parent")
        super(Parent, self).__init__()

class Left(Parent):
    def __init__(self):
        print("left")

class Right(Parent):
    def __init__(self):
        print("right")
        super(Right, self).__init__()

class Child(Left, Right):
    def __init__(self):
        print("child")
        super(Child, self).__init__()

Child()< /code> 输出:

child
left

或者,如果 Right 不调用 super,则仍会跳过 Parent

class Parent(object):
    def __init__(self):
        print("parent")
        super(Parent, self).__init__()

class Left(Parent):
    def __init__(self):
        print("left")
        super(Left, self).__init__()

class Right(Parent):
    def __init__(self):
        print("right")

class Child(Left, Right):
    def __init__(self):
        print("child")
        super(Child, self).__init__()

这里,Child()< /code> 输出:

child
left
right

调用特定父类的方法

如果您想访问特定父类的方法,您应该直接引用该类,而不是使用 super。 Super 是关于遵循继承链,而不是到达特定类的方法。

以下是引用特定父级方法的方法:

class Parent(object):
    def __init__(self):
        super(Parent, self).__init__()
        print("parent")

class Left(Parent):
    def __init__(self):
        super(Left, self).__init__()
        print("left")

class Right(Parent):
    def __init__(self):
        super(Right, self).__init__()
        print("right")

class Child(Left, Right):
    def __init__(self):
        Parent.__init__(self)
        print("child")

在本例中,Child() 输出:

parent
child

Overall

Assuming everything descends from object (you are on your own if it doesn't), Python computes a method resolution order (MRO) based on your class inheritance tree. The MRO satisfies 3 properties:

  • Children of a class come before their parents
  • Left parents come before right parents
  • A class only appears once in the MRO

If no such ordering exists, Python errors. The inner workings of this is a C3 Linerization of the classes ancestry. Read all about it here: https://www.python.org/download/releases/2.3/mro/

When a method is called, the first occurrence of that method in the MRO is the one that is called. Any class that doesn't implement that method is skipped. Any call to super within that method will call the next occurrence of that method in the MRO. Consequently, it matters both what order you place classes in inheritance, and where you put the calls to super in the methods.

Note that you can see the MRO in python by using the __mro__ method.

Examples

All of the following examples have a diamond inheritance of classes like so:

    Parent
    /   \
   /     \
Left    Right
   \     /
    \   /
    Child

The MRO is:

  1. Child
  2. Left
  3. Right
  4. Parent

You can test this by calling Child.__mro__, which returns:

(__main__.Child, __main__.Left, __main__.Right, __main__.Parent, object)

With super first in each method

class Parent(object):
    def __init__(self):
        super(Parent, self).__init__()
        print("parent")

class Left(Parent):
    def __init__(self):
        super(Left, self).__init__()
        print("left")

class Right(Parent):
    def __init__(self):
        super(Right, self).__init__()
        print("right")

class Child(Left, Right):
    def __init__(self):
        super(Child, self).__init__()
        print("child")

Child() outputs:

parent
right
left
child
    

With super last in each method

class Parent(object):
    def __init__(self):
        print("parent")
        super(Parent, self).__init__()

class Left(Parent):
    def __init__(self):
        print("left")
        super(Left, self).__init__()

class Right(Parent):
    def __init__(self):
        print("right")
        super(Right, self).__init__()

class Child(Left, Right):
    def __init__(self):
        print("child")
        super(Child, self).__init__()

Child() outputs:

child
left
right
parent

When not all classes call super

Inheritance order matters most if not all classes in the chain of inheritance call super. For example, if Left doesn't call super, then the method on Right and Parent are never called:

class Parent(object):
    def __init__(self):
        print("parent")
        super(Parent, self).__init__()

class Left(Parent):
    def __init__(self):
        print("left")

class Right(Parent):
    def __init__(self):
        print("right")
        super(Right, self).__init__()

class Child(Left, Right):
    def __init__(self):
        print("child")
        super(Child, self).__init__()

Child() outputs:

child
left

Alternatively, if Right doesn't call super, Parent is still skipped:

class Parent(object):
    def __init__(self):
        print("parent")
        super(Parent, self).__init__()

class Left(Parent):
    def __init__(self):
        print("left")
        super(Left, self).__init__()

class Right(Parent):
    def __init__(self):
        print("right")

class Child(Left, Right):
    def __init__(self):
        print("child")
        super(Child, self).__init__()

Here, Child() outputs:

child
left
right

Calling a method on a particular parent

If you want to access the method of a particular parent class, you should reference that class directly rather than using super. Super is about following the chain of inheritance, not getting to a specific class's method.

Here's how to reference a particular parent's method:

class Parent(object):
    def __init__(self):
        super(Parent, self).__init__()
        print("parent")

class Left(Parent):
    def __init__(self):
        super(Left, self).__init__()
        print("left")

class Right(Parent):
    def __init__(self):
        super(Right, self).__init__()
        print("right")

class Child(Left, Right):
    def __init__(self):
        Parent.__init__(self)
        print("child")

In this case, Child() outputs:

parent
child
望喜 2024-09-17 13:12:53

这被称为钻石问题,该页面有一个关于Python的条目,但简而言之,Python将从左到右调用超类的方法。

This is known as the Diamond Problem, the page has an entry on Python, but in short, Python will call the superclass's methods from left to right.

合久必婚 2024-09-17 13:12:53

我知道这并不能直接回答 super() 问题,但我觉得它足够相关,值得分享。

还有一种方法可以直接调用每个继承的类:


class First(object):
    def __init__(self):
        print '1'

class Second(object):
    def __init__(self):
        print '2'

class Third(First, Second):
    def __init__(self):
        Second.__init__(self)

请注意,如果您这样做,则必须手动调用每个类,因为我很确定 First__init__ () 不会被调用。

I understand this doesn't directly answer the super() question, but I feel it's relevant enough to share.

There is also a way to directly call each inherited class:


class First(object):
    def __init__(self):
        print '1'

class Second(object):
    def __init__(self):
        print '2'

class Third(First, Second):
    def __init__(self):
        Second.__init__(self)

Just note that if you do it this way, you'll have to call each manually as I'm pretty sure First's __init__() won't be called.

始于初秋 2024-09-17 13:12:53

这就是我如何解决具有不同初始化变量的多重继承以及具有相同函数调用的多个 MixIn 的问题。我必须显式地将变量添加到传递的 **kwargs 中,并添加 MixIn 接口作为超级调用的端点。

这里A是一个可扩展的基类,BC是MixIn类,它们都提供函数fAB 都在其 __init__ 中期望参数 v,而 C 则期望 >w
函数f 采用一个参数yQ 继承自所有三个类。 MixInFBC 的 mixin 接口。


class A(object):
    def __init__(self, v, *args, **kwargs):
        print "A:init:v[{0}]".format(v)
        kwargs['v']=v
        super(A, self).__init__(*args, **kwargs)
        self.v = v


class MixInF(object):
    def __init__(self, *args, **kwargs):
        print "IObject:init"
    def f(self, y):
        print "IObject:y[{0}]".format(y)


class B(MixInF):
    def __init__(self, v, *args, **kwargs):
        print "B:init:v[{0}]".format(v)
        kwargs['v']=v
        super(B, self).__init__(*args, **kwargs)
        self.v = v
    def f(self, y):
        print "B:f:v[{0}]:y[{1}]".format(self.v, y)
        super(B, self).f(y)


class C(MixInF):
    def __init__(self, w, *args, **kwargs):
        print "C:init:w[{0}]".format(w)
        kwargs['w']=w
        super(C, self).__init__(*args, **kwargs)
        self.w = w
    def f(self, y):
        print "C:f:w[{0}]:y[{1}]".format(self.w, y)
        super(C, self).f(y)


class Q(C,B,A):
    def __init__(self, v, w):
        super(Q, self).__init__(v=v, w=w)
    def f(self, y):
        print "Q:f:y[{0}]".format(y)
        super(Q, self).f(y)

This is to how I solved to issue of having multiple inheritance with different variables for initialization and having multiple MixIns with the same function call. I had to explicitly add variables to passed **kwargs and add a MixIn interface to be an endpoint for super calls.

Here A is an extendable base class and B and C are MixIn classes both who provide function f. A and B both expect parameter v in their __init__ and C expects w.
The function f takes one parameter y. Q inherits from all three classes. MixInF is the mixin interface for B and C.


class A(object):
    def __init__(self, v, *args, **kwargs):
        print "A:init:v[{0}]".format(v)
        kwargs['v']=v
        super(A, self).__init__(*args, **kwargs)
        self.v = v


class MixInF(object):
    def __init__(self, *args, **kwargs):
        print "IObject:init"
    def f(self, y):
        print "IObject:y[{0}]".format(y)


class B(MixInF):
    def __init__(self, v, *args, **kwargs):
        print "B:init:v[{0}]".format(v)
        kwargs['v']=v
        super(B, self).__init__(*args, **kwargs)
        self.v = v
    def f(self, y):
        print "B:f:v[{0}]:y[{1}]".format(self.v, y)
        super(B, self).f(y)


class C(MixInF):
    def __init__(self, w, *args, **kwargs):
        print "C:init:w[{0}]".format(w)
        kwargs['w']=w
        super(C, self).__init__(*args, **kwargs)
        self.w = w
    def f(self, y):
        print "C:f:w[{0}]:y[{1}]".format(self.w, y)
        super(C, self).f(y)


class Q(C,B,A):
    def __init__(self, v, w):
        super(Q, self).__init__(v=v, w=w)
    def f(self, y):
        print "Q:f:y[{0}]".format(y)
        super(Q, self).f(y)
秋心╮凉 2024-09-17 13:12:53

关于@calfzhou的评论,您可以使用,通常, **kwargs:

在线运行示例

class A(object):
  def __init__(self, a, *args, **kwargs):
    print("A", a)

class B(A):
  def __init__(self, b, *args, **kwargs):
    super(B, self).__init__(*args, **kwargs)
    print("B", b)

class A1(A):
  def __init__(self, a1, *args, **kwargs):
    super(A1, self).__init__(*args, **kwargs)
    print("A1", a1)

class B1(A1, B):
  def __init__(self, b1, *args, **kwargs):
    super(B1, self).__init__(*args, **kwargs)
    print("B1", b1)


B1(a1=6, b1=5, b="hello", a=None)

结果:

A None
B hello
A1 6
B1 5

也可以使用他们的位置:

B1(5, 6, b="hello", a=None)

但你必须记住 MRO,这真的很令人困惑。您可以通过使用仅限关键字参数来避免这种情况:

class A(object):
  def __init__(self, *args, a, **kwargs):
    print("A", a)

等等。

我可能有点烦人,但我注意到人们每次重写方法时都会忘记使用 *args**kwargs ,而这是少数真正有用的方法之一并合理使用这些“神奇变量”。

About @calfzhou's comment, you can use, as usually, **kwargs:

Online running example

class A(object):
  def __init__(self, a, *args, **kwargs):
    print("A", a)

class B(A):
  def __init__(self, b, *args, **kwargs):
    super(B, self).__init__(*args, **kwargs)
    print("B", b)

class A1(A):
  def __init__(self, a1, *args, **kwargs):
    super(A1, self).__init__(*args, **kwargs)
    print("A1", a1)

class B1(A1, B):
  def __init__(self, b1, *args, **kwargs):
    super(B1, self).__init__(*args, **kwargs)
    print("B1", b1)


B1(a1=6, b1=5, b="hello", a=None)

Result:

A None
B hello
A1 6
B1 5

You can also use them positionally:

B1(5, 6, b="hello", a=None)

but you have to remember the MRO, it's really confusing. You can avoid this by using keyword-only parameters:

class A(object):
  def __init__(self, *args, a, **kwargs):
    print("A", a)

etcetera.

I can be a little annoying, but I noticed that people forgot every time to use *args and **kwargs when they override a method, while it's one of few really useful and sane use of these 'magic variables'.

白日梦 2024-09-17 13:12:53

另一个尚未涉及的点是传递类初始化的参数。由于 super 的目的地取决于子类,因此传递参数的唯一好方法是将它们全部打包在一起。然后注意不要有相同的参数名称但含义不同。

示例:

class A(object):
    def __init__(self, **kwargs):
        print('A.__init__')
        super().__init__()

class B(A):
    def __init__(self, **kwargs):
        print('B.__init__ {}'.format(kwargs['x']))
        super().__init__(**kwargs)


class C(A):
    def __init__(self, **kwargs):
        print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
        super().__init__(**kwargs)


class D(B, C): # MRO=D, B, C, A
    def __init__(self):
        print('D.__init__')
        super().__init__(a=1, b=2, x=3)

print(D.mro())
D()

给出:

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__

直接调用超类 __init__ 来更直接地分配参数是很诱人的,但如果超类中有任何 super 调用和/或 MRO 是,则会失败更改并且类 A 可能会被多次调用,具体取决于实现。

总而言之:协作继承以及用于初始化的超级参数和特定参数不能很好地协同工作。

Another not yet covered point is passing parameters for initialization of classes. Since the destination of super depends on the subclass the only good way to pass parameters is packing them all together. Then be careful to not have the same parameter name with different meanings.

Example:

class A(object):
    def __init__(self, **kwargs):
        print('A.__init__')
        super().__init__()

class B(A):
    def __init__(self, **kwargs):
        print('B.__init__ {}'.format(kwargs['x']))
        super().__init__(**kwargs)


class C(A):
    def __init__(self, **kwargs):
        print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
        super().__init__(**kwargs)


class D(B, C): # MRO=D, B, C, A
    def __init__(self):
        print('D.__init__')
        super().__init__(a=1, b=2, x=3)

print(D.mro())
D()

gives:

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__

Calling the super class __init__ directly to more direct assignment of parameters is tempting but fails if there is any super call in a super class and/or the MRO is changed and class A may be called multiple times, depending on the implementation.

To conclude: cooperative inheritance and super and specific parameters for initialization aren't working together very well.

趁年轻赶紧闹 2024-09-17 13:12:53

考虑调用从子类调用的 super().Foo()方法解析顺序 (MRO) 方法是解析方法调用的顺序。

情况 1:单一继承

在这种情况下, super().Foo() 将在层次结构中向上搜索,并考虑最接近的实现(如果找到),否则引发异常。在任何访问的子类与其层次结构中的超类之间,“是一个”关系始终为 True。但这个故事在多重继承中并不总是一样。

情况 2:多重继承

这里,在搜索 super().Foo() 实现时,层次结构中的每个访问的类可能有也可能没有 is 关系。考虑以下示例:

class A(object): pass
class B(object): pass
class C(A): pass
class D(A): pass
class E(C, D): pass
class F(B): pass
class G(B): pass
class H(F, G): pass
class I(E, H): pass

这里,I 是层次结构中的最低类。 I 的层次结构图和 MRO 将为

在此处输入图像描述

(红色数字显示 MRO)

MRO 是 IECDAHFGB 对象

请注意,类 X(即,您永远不应该访问一个类,该类的箭头从您尚未访问过的下面的类进入)。

这里,请注意,在访问类 C 后,会访问 D,尽管 CD 没有 它们之间是关系(但都与A有关系)。这就是 super() 与单一继承的不同之处。

考虑一个稍微复杂的示例:

在此处输入图像描述

(红色数字显示 MRO)

MRO 是 IECHDAFGB 对象

在这种情况下,我们从 I 继续到 EC。下一步将是 A,但我们还没有访问 D,它是 A 的子类。但是,我们无法访问 D,因为我们还没有访问 H,它是 D 的子类。将 H 作为下一个要访问的类。请记住,如果可能的话,我们尝试在层次结构中向上移动,因此我们访问其最左边的超类,D。在D之后,我们访问了A,但是我们无法到达对象,因为我们还没有访问FGB。这些类按顺序完善了 I 的 MRO。

请注意,任何类别在 MRO 中都不能出现多次。

这就是 super() 在继承层次结构中查找的方式。

资源来源:Richard L Halterman Python 编程基础

Consider calling super().Foo() called from a sub-class. The Method Resolution Order (MRO) method is the order in which method calls are resolved.

Case 1: Single Inheritance

In this, super().Foo() will be searched up in the hierarchy and will consider the closest implementation, if found, else raise an Exception. The "is a" relationship will always be True in between any visited sub-class and its super class up in the hierarchy. But this story isn't the same always in Multiple Inheritance.

Case 2: Multiple Inheritance

Here, while searching for super().Foo() implementation, every visited class in the hierarchy may or may not have is a relation. Consider following examples:

class A(object): pass
class B(object): pass
class C(A): pass
class D(A): pass
class E(C, D): pass
class F(B): pass
class G(B): pass
class H(F, G): pass
class I(E, H): pass

Here, I is the lowest class in the hierarchy. Hierarchy diagram and MRO for I will be

enter image description here

(Red numbers showing the MRO)

MRO is I E C D A H F G B object

Note that a class X will be visited only if all its sub-classes, which inherit from it, have been visited(i.e., you should never visit a class that has an arrow coming into it from a class below that you have not yet visited).

Here, note that after visiting class C , D is visited although C and D DO NOT have is a relationship between them(but both have with A). This is where super() differs from single inheritance.

Consider a slightly more complicated example:

enter image description here

(Red numbers showing the MRO)

MRO is I E C H D A F G B object

In this case we proceed from I to E to C. The next step up would be A, but we have yet to visit D, a subclass of A. We cannot visit D, however, because we have yet to visit H, a subclass of D. The leaves H as the next class to visit. Remember, we attempt to go up in hierarchy, if possible, so we visit its leftmost superclass, D. After D we visit A, but we cannot go up to object because we have yet to visit F, G, and B. These classes, in order, round out the MRO for I.

Note that no class can appear more than once in MRO.

This is how super() looks up in the hierarchy of inheritance.

Credits for resources: Richard L Halterman Fundamentals of Python Programming

请帮我爱他 2024-09-17 13:12:53

如果您尝试继承的每个类都有自己的 init 位置参数,只需调用每个类自己的 init 方法,并且如果尝试从多个对象继承,则不要使用 super 。

class A():
    def __init__(self, x):
        self.x = x

class B():
    def __init__(self, y, z):
        self.y = y
        self.z = z

class C(A, B):
    def __init__(self, x, y, z):
        A.__init__(self, x)
        B.__init__(self, y, z)

>>> c = C(1,2,3)
>>>c.x, c.y, c.z 
(1, 2, 3)

In the case where each class you're trying to inherit from has it's own positional arguments for it's init, simply call each class's own init method, and don't use super if trying to inherit from multiple objects.

class A():
    def __init__(self, x):
        self.x = x

class B():
    def __init__(self, y, z):
        self.y = y
        self.z = z

class C(A, B):
    def __init__(self, x, y, z):
        A.__init__(self, x)
        B.__init__(self, y, z)

>>> c = C(1,2,3)
>>>c.x, c.y, c.z 
(1, 2, 3)
原来是傀儡 2024-09-17 13:12:53

考虑子 AB,其中父 AB 在其构造函数中具有关键字参数。

  A    B
   \  /
    AB

要初始化AB,您需要显式调用父类构造函数,而不是使用super()

示例:

class A():
    def __init__(self, a="a"):
        self.a = a
        print(f"a={a}")
    
    def A_method(self):
        print(f"A_method: {self.a}")

class B():
    def __init__(self, b="b"):
        self.b = b
        print(f"b={b}")
    
    def B_method(self):
        print(f"B_method: {self.b}")
    
    def magical_AB_method(self):
        print(f"magical_AB_method: {self.a}, {self.b}")

class AB(A,B):
    def __init__(self, a="A", b="B"):
        # super().__init__(a=a, b=b) # fails!
        A.__init__(self,a=a)
        B.__init__(self,b=b)
        self.A_method()
        self.B_method()


A()
>>> a=a

B()
>>> b=b

AB()
>>> a=A
>>> b=B
>>> A_method: A
>>> B_method: B

为了演示两个父级合并为子级,请考虑在类 B 中定义的 magical_AB_method。当从 B 的实例调用时,该方法会失败,因为它无权访问 A 内部的成员变量。但是,当从子 AB 的实例调用时,此方法可以工作,因为它从 A 继承了所需的成员变量。

B().magical_AB_method()
>>> AttributeError: 'B' object has no attribute 'a'

AB().magical_AB_method()
>>> magical_AB_method: A, B

Consider child AB, where parents A and B have keyword arguments in their constructors.

  A    B
   \  /
    AB

To init AB, you need to call the parent class constructors explicitly instead of using super().

Example:

class A():
    def __init__(self, a="a"):
        self.a = a
        print(f"a={a}")
    
    def A_method(self):
        print(f"A_method: {self.a}")

class B():
    def __init__(self, b="b"):
        self.b = b
        print(f"b={b}")
    
    def B_method(self):
        print(f"B_method: {self.b}")
    
    def magical_AB_method(self):
        print(f"magical_AB_method: {self.a}, {self.b}")

class AB(A,B):
    def __init__(self, a="A", b="B"):
        # super().__init__(a=a, b=b) # fails!
        A.__init__(self,a=a)
        B.__init__(self,b=b)
        self.A_method()
        self.B_method()


A()
>>> a=a

B()
>>> b=b

AB()
>>> a=A
>>> b=B
>>> A_method: A
>>> B_method: B

To demonstrate that the two parents are combined into the child, consider magical_AB_method defined inside class B. When called from an instance of B, the method fails since it does not have access to member variables inside A. However, when called from an instance of child AB, this method works since it has inherited the required member variable from A.

B().magical_AB_method()
>>> AttributeError: 'B' object has no attribute 'a'

AB().magical_AB_method()
>>> magical_AB_method: A, B
国际总奸 2024-09-17 13:12:53

在 python 3.5+ 中,继承看起来是可预测的,对我来说非常好。
请看一下这段代码:

class Base(object):
  def foo(self):
    print("    Base(): entering")
    print("    Base(): exiting")


class First(Base):
  def foo(self):
    print("   First(): entering Will call Second now")
    super().foo()
    print("   First(): exiting")


class Second(Base):
  def foo(self):
    print("  Second(): entering")
    super().foo()
    print("  Second(): exiting")


class Third(First, Second):
  def foo(self):
    print(" Third(): entering")
    super().foo()
    print(" Third(): exiting")


class Fourth(Third):
  def foo(self):
    print("Fourth(): entering")
    super().foo()
    print("Fourth(): exiting")

Fourth().foo()
print(Fourth.__mro__)

输出:

Fourth(): entering
 Third(): entering
   First(): entering Will call Second now
  Second(): entering
    Base(): entering
    Base(): exiting
  Second(): exiting
   First(): exiting
 Third(): exiting
Fourth(): exiting
(<class '__main__.Fourth'>, <class '__main__.Third'>, <class '__main__.First'>, <class '__main__.Second'>, <class '__main__.Base'>, <class 'object'>)

如您所见,它按照继承的顺序为每个继承的链调用 foo 一次。您可以通过调用 来获取该订单。mro< /a> :

第四个 ->第三->首先->第二->基地 ->目的

In python 3.5+ inheritance looks predictable and very nice for me.
Please looks at this code:

class Base(object):
  def foo(self):
    print("    Base(): entering")
    print("    Base(): exiting")


class First(Base):
  def foo(self):
    print("   First(): entering Will call Second now")
    super().foo()
    print("   First(): exiting")


class Second(Base):
  def foo(self):
    print("  Second(): entering")
    super().foo()
    print("  Second(): exiting")


class Third(First, Second):
  def foo(self):
    print(" Third(): entering")
    super().foo()
    print(" Third(): exiting")


class Fourth(Third):
  def foo(self):
    print("Fourth(): entering")
    super().foo()
    print("Fourth(): exiting")

Fourth().foo()
print(Fourth.__mro__)

Outputs:

Fourth(): entering
 Third(): entering
   First(): entering Will call Second now
  Second(): entering
    Base(): entering
    Base(): exiting
  Second(): exiting
   First(): exiting
 Third(): exiting
Fourth(): exiting
(<class '__main__.Fourth'>, <class '__main__.Third'>, <class '__main__.First'>, <class '__main__.Second'>, <class '__main__.Base'>, <class 'object'>)

As you can see, it calls foo exactly ONE time for each inherited chain in the same order as it was inherited. You can get that order by calling .mro :

Fourth -> Third -> First -> Second -> Base -> object

洒一地阳光 2024-09-17 13:12:53
class First(object):
  def __init__(self, a):
    print "first", a
    super(First, self).__init__(20)

class Second(object):
  def __init__(self, a):
    print "second", a
    super(Second, self).__init__()

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__(10)
    print "that's it"

t = Third()

输出是

first 10
second 20
that's it

调用 Third() 定位 Third 中定义的 init。并在该例程中调用 super 调用 First 中定义的 init 。 MRO=[第一,第二]。
现在调用First中定义的init中的super将继续搜索MRO并找到Second中定义的init,并且任何对super的调用都会命中默认对象init。我希望这个例子能够澄清这个概念。

如果你不从 First 调用 super。链停止,您将得到以下输出。

first 10
that's it
class First(object):
  def __init__(self, a):
    print "first", a
    super(First, self).__init__(20)

class Second(object):
  def __init__(self, a):
    print "second", a
    super(Second, self).__init__()

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__(10)
    print "that's it"

t = Third()

Output is

first 10
second 20
that's it

Call to Third() locates the init defined in Third. And call to super in that routine invokes init defined in First. MRO=[First, Second].
Now call to super in init defined in First will continue searching MRO and find init defined in Second, and any call to super will hit the default object init. I hope this example clarifies the concept.

If you don't call super from First. The chain stops and you will get the following output.

first 10
that's it
猫弦 2024-09-17 13:12:53

我想在顶部添加 @Visionscaper 所说的

Third --> First --> object --> Second --> object

在这种情况下,解释器不会过滤掉对象类,因为它是重复的,而是因为 Second 出现在层次子集中的头部位置并且没有出现在尾部位置。而对象仅出现在尾部位置,在 C3 算法中不被视为确定优先级的强位置。

C 类 L(C) 的线性化 (mro) 是

  • C 类
  • 加上以下的合并
    • 其父项 P1, P2, .. = L(P1, P2, ...) 的线性化并且
    • 其父项列表 P1、P2、..

线性化合并是通过选择显示为列表的头部而不是尾部,因为顺序很重要(下面将变得清楚)

第三个的线性化可以计算如下:

    L(O)  := [O]  // the linearization(mro) of O(object), because O has no parents

    L(First)  :=  [First] + merge(L(O), [O])
               =  [First] + merge([O], [O])
               =  [First, O]

    // Similarly, 
    L(Second)  := [Second, O]

    L(Third)   := [Third] + merge(L(First), L(Second), [First, Second])
                = [Third] + merge([First, O], [Second, O], [First, Second])
// class First is a good candidate for the first merge step, because it only appears as the head of the first and last lists
// class O is not a good candidate for the next merge step, because it also appears in the tails of list 1 and 2, 
                = [Third, First] + merge([O], [Second, O], [Second])
// class Second is a good candidate for the second merge step, because it appears as the head of the list 2 and 3
                = [Third, First, Second] + merge([O], [O])            
                = [Third, First, Second, O]

因此,对于以下代码中的 super() 实现:

class First(object):
  def __init__(self):
    super(First, self).__init__()
    print "first"

class Second(object):
  def __init__(self):
    super(Second, self).__init__()
    print "second"

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__()
    print "that's it"

很明显如何解决该方法

Third.__init__() ---> First.__init__() ---> Second.__init__() ---> 
Object.__init__() ---> returns ---> Second.__init__() -
prints "second" - returns ---> First.__init__() -
prints "first" - returns ---> Third.__init__() - prints "that's it"

I would like to add to what @Visionscaper says at the top:

Third --> First --> object --> Second --> object

In this case the interpreter doesnt filter out the object class because its duplicated, rather its because Second appears in a head position and doesnt appear in the tail position in a hierarchy subset. While object only appears in tail positions and is not considered a strong position in C3 algorithm to determine priority.

The linearisation(mro) of a class C, L(C), is the

  • the Class C
  • plus the merge of
    • linearisation of its parents P1, P2, .. = L(P1, P2, ...) and
    • the list of its parents P1, P2, ..

Linearised Merge is done by selecting the common classes that appears as the head of lists and not the tail since order matters(will become clear below)

The linearisation of Third can be computed as follows:

    L(O)  := [O]  // the linearization(mro) of O(object), because O has no parents

    L(First)  :=  [First] + merge(L(O), [O])
               =  [First] + merge([O], [O])
               =  [First, O]

    // Similarly, 
    L(Second)  := [Second, O]

    L(Third)   := [Third] + merge(L(First), L(Second), [First, Second])
                = [Third] + merge([First, O], [Second, O], [First, Second])
// class First is a good candidate for the first merge step, because it only appears as the head of the first and last lists
// class O is not a good candidate for the next merge step, because it also appears in the tails of list 1 and 2, 
                = [Third, First] + merge([O], [Second, O], [Second])
// class Second is a good candidate for the second merge step, because it appears as the head of the list 2 and 3
                = [Third, First, Second] + merge([O], [O])            
                = [Third, First, Second, O]

Thus for a super() implementation in the following code:

class First(object):
  def __init__(self):
    super(First, self).__init__()
    print "first"

class Second(object):
  def __init__(self):
    super(Second, self).__init__()
    print "second"

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__()
    print "that's it"

it becomes obvious how this method will be resolved

Third.__init__() ---> First.__init__() ---> Second.__init__() ---> 
Object.__init__() ---> returns ---> Second.__init__() -
prints "second" - returns ---> First.__init__() -
prints "first" - returns ---> Third.__init__() - prints "that's it"
空‖城人不在 2024-09-17 13:12:53

在学习 pythonthehardway 的过程中,我学习了一个叫做 super() 的东西,如果没有弄错的话,这是一个内置函数。调用 super() 函数可以帮助继承穿过父级和“兄弟姐妹”,并帮助您看得更清楚。我仍然是一个初学者,但我喜欢分享我在 python2.7 中使用这个 super() 的经验。

如果你读过本页的注释,你会听说方法解析顺序(MRO),该方法就是你编写的函数,MRO将使用深度优先从左到右的方案来搜索和运行。您可以对此进行更多研究。

通过添加 super() 函数

super(First, self).__init__() #example for class First.

您可以通过添加其中的每个实例和“族”来使用 super() 连接多个实例和“族”。它将执行这些方法,遍历它们并确保您没有错过!然而,在之前或之后添加它们确实会产生影响,如果您完成了学习Python的困难练习44,您就会知道。让乐趣开始吧!

以下面的示例为例,您可以复制并添加粘贴并尝试运行它:

class First(object):
    def __init__(self):

        print("first")

class Second(First):
    def __init__(self):
        print("second (before)")
        super(Second, self).__init__()
        print("second (after)")

class Third(First):
    def __init__(self):
        print("third (before)")
        super(Third, self).__init__()
        print("third (after)")


class Fourth(First):
    def __init__(self):
        print("fourth (before)")
        super(Fourth, self).__init__()
        print("fourth (after)")


class Fifth(Second, Third, Fourth):
    def __init__(self):
        print("fifth (before)")
        super(Fifth, self).__init__()
        print("fifth (after)")

Fifth()

它是如何运行的? Fifth() 的实例将如下所示。每个步骤都是从添加了 super 函数的类到类。

1.) print("fifth (before)")
2.) super()>[Second, Third, Fourth] (Left to right)
3.) print("second (before)")
4.) super()> First (First is the Parent which inherit from object)

找到了父母,并将继续第三和第四!

5.) print("third (before)")
6.) super()> First (Parent class)
7.) print ("Fourth (before)")
8.) super()> First (Parent class)

现在所有带有 super() 的类都已被访问!父类已经找到并执行,现在继续拆箱继承中的函数以完成代码。

9.) print("first") (Parent)
10.) print ("Fourth (after)") (Class Fourth un-box)
11.) print("third (after)") (Class Third un-box)
12.) print("second (after)") (Class Second un-box)
13.) print("fifth (after)") (Class Fifth un-box)
14.) Fifth() executed

上面程序的结果:

fifth (before)
second (before
third (before)
fourth (before)
first
fourth (after)
third (after)
second (after)
fifth (after)

对我来说,通过添加 super() 可以让我更清楚地了解 python 将如何执行我的编码,并确保继承可以访问我想要的方法。

In learningpythonthehardway I learn something called super() an in-built function if not mistaken. Calling super() function can help the inheritance to pass through the parent and 'siblings' and help you to see clearer. I am still a beginner but I love to share my experience on using this super() in python2.7.

If you have read through the comments in this page, you will hear of Method Resolution Order (MRO), the method being the function you wrote, MRO will be using Depth-First-Left-to-Right scheme to search and run. You can do more research on that.

By adding super() function

super(First, self).__init__() #example for class First.

You can connect multiple instances and 'families' with super(), by adding in each and everyone in them. And it will execute the methods, go through them and make sure you didn't miss out! However, adding them before or after does make a difference you will know if you have done the learningpythonthehardway exercise 44. Let the fun begins!!

Taking example below, you can copy & paste and try run it:

class First(object):
    def __init__(self):

        print("first")

class Second(First):
    def __init__(self):
        print("second (before)")
        super(Second, self).__init__()
        print("second (after)")

class Third(First):
    def __init__(self):
        print("third (before)")
        super(Third, self).__init__()
        print("third (after)")


class Fourth(First):
    def __init__(self):
        print("fourth (before)")
        super(Fourth, self).__init__()
        print("fourth (after)")


class Fifth(Second, Third, Fourth):
    def __init__(self):
        print("fifth (before)")
        super(Fifth, self).__init__()
        print("fifth (after)")

Fifth()

How does it run? The instance of fifth() will goes like this. Each step goes from class to class where the super function added.

1.) print("fifth (before)")
2.) super()>[Second, Third, Fourth] (Left to right)
3.) print("second (before)")
4.) super()> First (First is the Parent which inherit from object)

The parent was found and it will go continue to Third and Fourth!!

5.) print("third (before)")
6.) super()> First (Parent class)
7.) print ("Fourth (before)")
8.) super()> First (Parent class)

Now all the classes with super() have been accessed! The parent class has been found and executed and now it continues to unbox the function in the inheritances to finished the codes.

9.) print("first") (Parent)
10.) print ("Fourth (after)") (Class Fourth un-box)
11.) print("third (after)") (Class Third un-box)
12.) print("second (after)") (Class Second un-box)
13.) print("fifth (after)") (Class Fifth un-box)
14.) Fifth() executed

The outcome of the program above:

fifth (before)
second (before
third (before)
fourth (before)
first
fourth (after)
third (after)
second (after)
fifth (after)

For me by adding super() allows me to see clearer on how python would execute my coding and make sure the inheritance can access the method I intended.

凑诗 2024-09-17 13:12:53

也许还可以添加一些东西,一个 Django Rest_framework 和装饰器的小例子。这为隐含的问题提供了答案:“为什么我想要这个?”

如前所述:我们使用 Django Rest_framework,并且使用通用视图,对于数据库中的每种类型的对象,我们发现自己有一个视图类为对象列表提供 GET 和 POST,另一个视图类提供 GET 、PUT 和 DELETE 用于单个对象。

现在我们想用Django的login_required来装饰POST、PUT和DELETE。请注意这如何涉及两个类,但不是两个类中的所有方法。

解决方案可以通过多重继承。

from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required

class LoginToPost:
    @method_decorator(login_required)
    def post(self, arg, *args, **kwargs):
        super().post(arg, *args, **kwargs)

对于其他方法也是如此。

在具体类的继承列表中,我将在 ListCreateAPIView 之前添加 LoginToPost,在 RetrieveUpdateDestroyAPIView 之前添加 LoginToPutOrDelete。我的具体类的 get 将保持未修饰状态。

Maybe there's still something that can be added, a small example with Django rest_framework, and decorators. This provides an answer to the implicit question: "why would I want this anyway?"

As said: we're with Django rest_framework, and we're using generic views, and for each type of objects in our database we find ourselves with one view class providing GET and POST for lists of objects, and an other view class providing GET, PUT, and DELETE for individual objects.

Now the POST, PUT, and DELETE we want to decorate with Django's login_required. Notice how this touches both classes, but not all methods in either class.

A solution could go through multiple inheritance.

from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required

class LoginToPost:
    @method_decorator(login_required)
    def post(self, arg, *args, **kwargs):
        super().post(arg, *args, **kwargs)

Likewise for the other methods.

In the inheritance list of my concrete classes, I would add my LoginToPost before ListCreateAPIView and LoginToPutOrDelete before RetrieveUpdateDestroyAPIView. My concrete classes' get would stay undecorated.

┼── 2024-09-17 13:12:53

发布这个答案以供我将来参考。

Python 多重继承应使用钻石模型,并且模型中的函数签名不应更改。

    A
   / \
  B   C
   \ /
    D

示例代码片段为 ;-

class A:
    def __init__(self, name=None):
        #  this is the head of the diamond, no need to call super() here
        self.name = name

class B(A):
    def __init__(self, param1='hello', **kwargs):
        super().__init__(**kwargs)
        self.param1 = param1

class C(A):
    def __init__(self, param2='bye', **kwargs):
        super().__init__(**kwargs)
        self.param2 = param2

class D(B, C):
    def __init__(self, works='fine', **kwargs):
        super().__init__(**kwargs)
        print(f"{works=}, {self.param1=}, {self.param2=}, {self.name=}")

d = D(name='Testing')

这里 A 类是 object

Posting this answer for my future referance.

Python Multiple Inheritance should use a diamond model and the function signature shouldn't change in the model.

    A
   / \
  B   C
   \ /
    D

The sample code snippet would be ;-

class A:
    def __init__(self, name=None):
        #  this is the head of the diamond, no need to call super() here
        self.name = name

class B(A):
    def __init__(self, param1='hello', **kwargs):
        super().__init__(**kwargs)
        self.param1 = param1

class C(A):
    def __init__(self, param2='bye', **kwargs):
        super().__init__(**kwargs)
        self.param2 = param2

class D(B, C):
    def __init__(self, works='fine', **kwargs):
        super().__init__(**kwargs)
        print(f"{works=}, {self.param1=}, {self.param2=}, {self.name=}")

d = D(name='Testing')

Here class A is object

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文