为什么这个继承结构是这样构建的呢?

Why is this inheritance structure built like this?

几天来我一直在努力理解这段代码的细节:

class Rectangle:
    def __init__(self, length, width, **kwargs):
        self.length = length
        self.width = width
        super().__init__(**kwargs)

    def area(self):
        return self.length * self.width

    def perimeter(self):
        return 2 * self.length + 2 * self.width

# Square inherits from Rectangle:
class Square(Rectangle):
    def __init__(self, length, **kwargs):
        super().__init__(length=length, width=length, **kwargs)


# Triangle doesn't inherit from any class:
class Triangle:
    def __init__(self, base, height, **kwargs):
        self.base = base
        self.height = height
        super().__init__(**kwargs) 

    def tri_area(self):
        return 0.5 * self.base * self.height

# Pyramid inherits from square and triangle:
class Pyramid(Square, Triangle):
    def __init__(self, base, slant_height, **kwargs):
        self.base = base
        self.slant_height = slant_height
        
        # Adding attributes to kwargs dictionary
        kwargs["height"] = slant_height
        kwargs["length"] = base
        super().__init__( base=base, **kwargs)

    def area(self):
        base_area = super().area()
        perimeter = super().perimeter()
        return 0.5 * perimeter * self.slant_height + base_area

    def area_2(self):
        base_area = super().area()
        triangle_area = super().tri_area()
        return triangle_area * 4 + base_area

继承树是这样的:

Rectangle 
    \
   Square  Triangle
      \     /
      Pyramid

Pyramid class 的 MRO:

(__main__.Pyramid,
 __main__.Square,
 __main__.Rectangle,
 __main__.Triangle,
 object)

我了解代码背后的总体思路,以及如何使用 super().__init__ 将可变关键字参数字典传递给使用 **kwargs 的 superclass。我遇到问题的详细信息是:

  1. 为什么我们在 Pyramid class 中使用 base=base 而 superclass (Square) 需要 length争论?
  2. 当我只输入 super().__init__(base, **kwargs) 而不是 base=base 时,为什么会报错 init() got参数 'length', 的多个值是因为上面指定的 kwargs["length"] 吗?如果是这样,base=base 是如何解决的?
  3. 为什么我们在 Rectangle 中没有 superclass 而有 super().__init__(**kwargs)?我的猜测可能是将 kwargs 传递给内置的 object 基 class 所以 Triangle 可以继承它?但我不确定。
  4. 为什么 Triangle 也会调用基地 class?这似乎是多余的(即使从 Triangle 中删除 super().__init__(**kwargs) 也不会改变任何东西)。

好问题!

  1. 因为 Square.__init__ 接受 **kwargs 我们实际上可以传递我们想要的任何东西而不会得到 unexpected keyword argument 错误1.这稍后会变得很方便。它(and/or 它的超类的 __init__ 方法)不使用的任何关键字参数都将在 MRO 中向上传播,直到我们到达 object.


    1通常。在这种情况下,(不必要的)调用

    super().__init__(**kwargs) in Triangle.__init__ 打破了这种行为。


  1. 由于kwargs的扩展,在Pyramid.__init__中调用super().__init__(base, **kwargs)时,Square.__init__实际上是用(2, height=3, length=2)调用的。由于 Square.__init__ 的参数称为 length,您会收到有关 length.

    的多个值的错误
  2. 没错。此外,因为还没有任何东西“消耗” base 关键字参数,所以 Triangle.__init__ 将接收它。

  3. 也对。绝对没有必要在 Triangle.__init__ 中调用 super().__init__(**kwargs),尤其是到那时 kwargs 是一个空字典(它包含的所有关键字参数都已经被消耗)。