Python 的 super() 如何与多重继承一起工作?

2024-11-20 08:44:00
admin
原创
6
摘要:问题描述:如何super()处理多重继承?例如,给定:class First(object): def __init__(self): print "first" class Second(object): def __init__(self): ...

问题描述:

如何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"

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

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


解决方案 1:

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
second hird"或其他目的。您可以(当然也应该)尝试一下这个示例,添加super()`调用,看看会发生什么,并更深入地了解 Python 的继承模型。但我的目标是保持简单并展示 MRO 是如何构建的。它的构建方式如下:

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

解决方案 2:

您的代码和其他答案都是错误的。它们缺少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__()


如果没有super()中的调用,则缺少输出:First`Second`second

>>> Third()
first
third

解决方案 3:

我想通过 lifeless 来详细解释一下答案,因为当我开始阅读如何在 Python 中的多重继承层次结构中使用 super() 时,我并没有立即明白。

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

最后一部分是理解的关键。让我们再考虑一下这个例子:

#!/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. 按照MROThird.__init__执行。

* 印刷`Third(): entering`
* 然后`super(Third, self).__init__()`执行并返回`First.__init__`被调用的 MRO。
  1. First.__init__执行。

* 印刷`First(): entering`
* 然后`super(First, self).__init__()`执行并返回`Second.__init__`被调用的 MRO。
  1. Second.__init__执行。

* 印刷`Second(): entering`
* 然后`super(Second, self).__init__()`执行并返回`object.__init__`被调用的 MRO。
  1. object.__init__执行(代码中没有打印语句)

  2. 执行返回到Second.__init__然后打印Second(): exiting

  3. 执行返回到First.__init__然后打印First(): exiting

  4. 执行返回到Third.__init__然后打印Third(): exiting

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

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

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

解决方案 4:

全面的

假设所有内容都来自object(如果不是,则您自己决定),Python 会根据您的类继承树计算方法解析顺序 (MRO)。MRO 满足 3 个属性:

  • 班级的孩子比父母先到

  • 左父母先于右父母

  • 一个类在 MRO 中只出现一次

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

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

注意,你可以使用该方法在python中查看MRO __mro__

示例

下面所有的例子都有菱形的类继承,像这样:

    Parent
    /   \n   /     \nLeft    Right
        /
       /
    Child

MRO 是:

  1. 孩子

  2. 左边

  3. 正确的

  4. 父母

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

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

super每个方法中都使用first

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每个方法中都有last

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。例如,如果不调用 super,则永远不会调用和Left上的方法:Right`Parent`

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()输出:

child
left

或者,如果Right没有调用superParent则仍然被跳过:

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()输出:

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

解决方案 5:

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

解决方案 6:

我知道这并不能直接回答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__()被调用。

解决方案 7:

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

A是一个可扩展的基类,和B都是C提供函数的MixIn类fA和都在它们的和期望中B期望参数。该函数接受一个参数。 从所有三个类继承。是和的mixin接口。v`__init__CwfyQMixInFBC`

  • 此代码的 IPython NoteBook

  • Github Repo 和代码示例


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)

解决方案 8:

关于@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覆盖方法,而这是这些“魔法变量”的少数真正有用和明智的用法之一。

解决方案 9:

另一个尚未涉及的要点是传递用于初始化类的参数。由于目标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 可能会被调用多次,具体取决于实现。

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

解决方案 10:

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

案例 1:单继承

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

案例 2:多重继承

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

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是层次结构中的最低类。层次结构图和 MROI将是

在此处输入图片描述

(红色数字表示 MRO)

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

X 请注意,仅当访问了从该类继承的所有子类时,才会访问该类(即,您永远不应该访问具有从您尚未访问过的下面的类进入的箭头的类)。

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

考虑一个稍微复杂一点的例子:

在此处输入图片描述

(红色数字表示 MRO)

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

在这种情况下,我们从I到进行EC。下一步将是A,但我们尚未访问D,它是的子类A。但是,我们不能访问D,因为我们尚未访问H,它是的子类D。叶子H是下一个要访问的类。请记住,如果可能,我们会尝试在层次结构中向上移动,因此我们访问其最左边的超类。DD访问之后A,但我们不能向上移动到对象,因为我们尚未访问FGB。这些类按顺序完成了 的 MRO I

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

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

资料来源:Richard L Halterman 的《Python 编程基础》

解决方案 11:

如果您尝试继承的每个类都有自己的 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)

解决方案 12:

考虑 child 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

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

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

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

解决方案 13:

在 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来获取该顺序:

第四个 -> 第三个 -> 第一 个 -> 第二 个 -> 基础 -> 对象

解决方案 14:

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, Second]。现在,在 First 中定义的init中调用 super将继续搜索 MRO 并找到Second 中定义的init,并且任何对 super 的调用都将命中默认对象init。我希望这个例子能阐明这个概念。

如果您没有从 First 调用 super。链将停止,您将获得以下输出。

first 10
that's it

解决方案 15:

我想补充一下@Visionscaper在文末所说的话:

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

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

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

  • 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"

解决方案 16:

在 learningpythonthehardway 中,我学到了一个叫做 super() 的内置函数,如果没记错的话。调用 super() 函数可以帮助继承通过父级和“兄弟”传递,并帮助您看得更清楚。我还是个初学者,但我很乐意分享我在 python2.7 中使用这个 super() 的经验。

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

通过添加 super() 函数

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

您可以使用 super() 连接多个实例和“家族”,方法是添加其中的每一个。它将执行方法,检查它们,并确保您没有遗漏!但是,在之前或之后添加它们确实会产生影响,如果您已经完成了 learningpythonthehardway 练习 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() 的实例将像这样运行。每一步都从一个类到另一个类,其中添加了超级函数。

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 如何执行我的编码,并确保继承可以访问我想要的方法。

解决方案 17:

也许还可以添加一些内容,比如一个带有 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)

其它方法同样如此。

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

解决方案 18:

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

Python 多重继承应该使用菱形模型,并且函数签名在模型中不应该改变。

    A
   / \n  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

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用