【问题标题】:Struggling with Class Inheritance & super().__init__与类继承和 super().__init__ 斗争
【发布时间】:2018-07-02 20:54:33
【问题描述】:

对 Python 非常陌生,试图创建一个可以创建任意数量军队的游戏,但每支军队都会预渲染士兵的名字。

我认为我需要使用超级初始化来真正减少重复代码,但我无法终生弄清楚如何让它工作。据我了解,我的类Army 应该是父类,RedArmyScout 作为子类。我只是在努力理解super().__init__() 应该在哪里出现?

class Army:    
    def __init__(self):
        self.color = None
        self.scoutname = None
        self.demomanname = None
        self.medicname = None

    def train_scout(self, weapon):
        return Scout(self.color, self.scoutname, weapon)

class RedArmy(Army):

    def __init__(self):
        self.color = "Red"
        self.scoutname = "Yankee"
        self.demomanname = "Irish"
        self.medicname = "Dutch"

class BlueArmy(Army):
    pass

class Scout:
    specialization = "fast captures"

    def __init__(self, color, scoutname, weapon):
        self.color = color
        self.scoutname = scoutname
        self.weapon = weapon

    def introduce(self):
        return (f'Hi I\'m {self.scoutname}, I do {self.specialization} and I wield a {self.weapon}')


my_army = RedArmy()    
soldier_1 = my_army.train_scout("baseball bat")
print(soldier_1.introduce())

【问题讨论】:

  • 注意:如果使用 Python-2.7,请将基类定义为 class Army(object),否则 super() 将不起作用。在 Python 3 上也可以使用 newclass(object)
  • @LeoK OP 显然使用的是 Python 3;如果没有,super() 无论如何都不会工作。教新手如何将新的、仅 Python-3 的代码编写为双版本代码会适得其反,因为这意味着错过了 Python 3 的所有改进。
  • @LeoK 如果使用 Python 2.7,Scout.introduce 中的 f-string 会导致语法错误。

标签: python inheritance


【解决方案1】:

super().__init__ 的放置位置(如果在任何地方)取决于您的类层次结构的具体情况。

最常见的情况是,如果放置位置很重要,您希望它位于子类的 __init__ 方法的最开始。这确保了所有基类变量都已设置,它的所有不变量都得到满足,并且它的所有方法都可以被子类__init__ 代码的其余部分调用。

就您而言,这很重要,因为您在基类和子类中设置了相同的属性。您显然希望子类版本成为需要的版本,因此它必须在默认分配之后,而不是之前:

class RedArmy(Army):
    def __init__(self):
        super().__init__()
        self.color = "Red"
        self.scoutname = "Yankee"
        self.demomanname = "Irish"
        self.medicname = "Dutch"

但是,值得考虑的是您是否真的希望基类首先将这些变量设置为None

我假设在您的真实代码中,BlueArmy 不仅仅是转到 pass,而是将执行与 RedArmy 相同的操作,将所有这些值替换为一些字符串。

此外,您的其余代码可能会假设那里有有效的字符串,而不是None。像TypeError: '<' not supported between instances of 'NoneType' and 'str' 这样的异常比AttributeError: 'GreenArmy' object has no attribute 'scoutname' 更难调试,而不是更容易,那么为什么不直接保留默认值呢?然后你可以完全消除Army.__init__,而且你不必担心首先在子类初始化器中调用super

或者,您可以让Army.__init__ 获取用于分配值的参数,并让子类调用super().__init__("Red", "Yankee", "Irish", "Dutch")。然后,Army() 将引发TypeError,而不是创建无效的Army 实例。或者你可以创建一个名为self._setup()@abstractmethodArmy.__init__ 调用并期望每个子类提供,所以Army() 将引发一个更有意义的TypeError 关于实例化抽象类。这些改进使调试Army 子类变得更容易——如果你只有两个,它们可能只是浪费时间,但如果你有一堆,它们将由不同的人或更多人开发很长一段时间,这可能是值得的。

【讨论】:

    【解决方案2】:

    如果您希望您的类利用 superinit,则将值作为参数传递是最有意义的:

    class Army:    
        def __init__(self, color=None, scoutname=None, demomanname=None,
                     medicname=None):
            self.color = color
            self.scoutname = scoutname
            self.demomanname = demomanname
            self.medicname = medicname
    
        def train_scout(self, weapon):
            return Scout(self.color, self.scoutname, weapon)
    
    
    class RedArmy(Army):
        def __init__(self, color="Red", scoutname="Yankee", demomanname="Irish",
                     medicname="Dutch"):
            super().__init__(color, scoutname, demomanname, medicname)
    
    
    class Scout:
        specialization = "fast captures"
    
        def __init__(self, color, scoutname, weapon):
            self.color = color
            self.scoutname = scoutname
            self.weapon = weapon
    
        def introduce(self):
            return (
                f'Hi I\'m {self.scoutname}, I do {self.specialization} and I wield '
                f'a {self.weapon}')
    
    
    my_army = RedArmy()    
    soldier_1 = my_army.train_scout("baseball bat")
    print(soldier_1.introduce())
    

    【讨论】:

      猜你喜欢
      • 2015-12-07
      • 1970-01-01
      • 2018-11-06
      • 2013-04-02
      • 2021-11-23
      • 2020-07-05
      • 2013-01-16
      • 2014-06-13
      • 2021-11-28
      相关资源
      最近更新 更多