【问题标题】:Why is this inheritance structure built like this?为什么要这样构建这个继承结构?
【发布时间】:2021-07-25 07:10:11
【问题描述】:

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

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 类的 MRO:

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

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

  1. 为什么我们在 Pyramid 类中使用 base=base 而超类 (Square) 采用 length 参数?
  2. 当我只放 super().__init__(base, **kwargs) 而不是 base=base 时,为什么会出现错误提示 init() 获得了参数“长度”的多个值, 是因为上面指定了kwargs["length"]吗?如果是这样,base=base 如何解决这个问题?
  3. 为什么在没有超类的 Rectangle 中有super().__init__(**kwargs)?我的猜测可能是将 kwargs 传递给内置的 object 基类,以便 Triangle 可以继承它?但我不确定。
  4. 为什么Triangle 也会调用基类?这似乎是多余的(即使从 Triangle 中删除 super().__init__(**kwargs) 也不会改变任何事情)。

【问题讨论】:

    标签: python inheritance multiple-inheritance


    【解决方案1】:

    好问题!

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


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

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


    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 是一个空字典(它包含的所有关键字参数都已被使用)。

    【讨论】:

    • 非常感谢您的详细回答!它澄清了很多事情。我只是想知道:1)为什么应该是base=base?因为当我尝试仅使用 baselength 时,我得到一个错误。 base=base 有什么特别之处。 2)当你说参数被消耗时,默认情况下,每当一个方法使用那个 kwarg 时才会发生这种消耗,还是只有当那个 kwarg 也用作位置参数时才会发生这种消耗(因为我已经读过这样做“pops”字典中的关键字参数)。谢谢!
    • @Metrician 1. base=base(或whatever=base)导致base包含的值作为关键字参数传递(在kwargs字典内). Without specifying it, it will be passed as the location`位置将导致冲突的参数(因为 location 已在 kwargs 字典中指定(答案中的第 2 点)。
    • @Metrician 2. 当一个方法接收到一个 kwargs dict 包含一个与其参数之一同名的键时,该参数(在此上下文中的键值对)将从kwargs 字典。您可以通过在示例中的 __init__ 方法中添加 print(kwargs) 来澄清这一点。
    • 非常感谢您抽出宝贵时间回复,现在一切都清楚了!让我摆脱了为期 2 天的头痛。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-19
    • 2021-04-06
    • 1970-01-01
    • 1970-01-01
    • 2010-11-16
    相关资源
    最近更新 更多