了解在子类中调用父__init__

时间:2015-08-13 18:44:04

标签: python class initialization subclass

Python的新手,如果有人能够以外行人的方式尽可能地解释为什么会这样,当你创建一个子类时,仍然需要调用父初始化函数,这很奇怪?

我们假设您有这样的父类:

class Pet(object):

    def __init__(self, name, species):
        self.name = name
        self.species = species

    def getName(self):
        return self.name

    def getSpecies(self):
        return self.species

    def __str__(self):
        return "%s is a %s" % (self.name, self.species)

然后我想创建一个名为" Dog"的子类。这显然使用了父类中的一些函数。我的研究告诉我,我需要做这样的事情:

class Dog(Pet):

    def __init__(self, name, chases_cats):
        Pet.__init__(self, name, "Dog")
        self.chases_cats = chases_cats

    def chasesCats(self):
        return self.chases_cats

但是,为什么,如果我在定义子类时将父类作为参数传递,我是否需要调用父__init__?这不是因为它是一个子类而自动传递给子类的吗?

我确定我在理解中遗漏了一些明显的东西,但它并没有点击我。

4 个答案:

答案 0 :(得分:2)

  

我要问的是:为什么这样做?如果您从父类继承,那么您是否可以继承父类中的所有内容,包括__init__

如果你压倒它,那就不行了。如果您的子类没有定义自己的__init__,那么确实调用了超类__init__。但是如果子类确实定义了它自己的__init__,那么你就会覆盖它,因此Python不会假设您想要如何或是否要调用超类实现。

这为您提供了额外的灵活性。如果自动调用超类__init__,则必须在子类__init__之前或之后统一调用它。但由于它不会自动调用,您可以随时调用它。您可以在子类__init__的开头,最后,中间某处或根本不调用它。这意味着你的子类可以在超类方法行为之前“设置”事物,或者在超类方法行为之后“调整”事物,或者同时执行两者,或者完全替换超类功能。

此外,如果自动调用超类实现,如果子类不接受与超类相同的参数,则不清楚该怎么做。使用显式超类调用,您的子类可以接受比其超类更多或更少的参数,并且可以自己决定在调用超类时要传递的内容。

答案 1 :(得分:1)

因为父类可能会在它的构造函数中做一些有趣的事情。当您使用自己的构造函数定义Pet的子类时,您将使用新构造函数覆盖该行为,因此保存宠物的名称和种类永远不会发生。要解决这个问题,你可以调用父类的构造函数作为你在新构造函数中做的第一件事,恢复保存名称和物种的重要行为。

您可能想知道,为什么不让每个子类单独保存宠物的名称和种类? (抱歉,如果你不是 - 希望这个答案可以帮助那些人。)编程中的原则叫做DRY - 不要重复自己。由于保存名称和物种的行为对所有宠物来说都很常见,因此在Pet类中实现它是有意义的。如果你说子类必须自己做,那么1.你正在重复自己,2。你必须相信自己要记住在你以后创建一个新的Pet子类时这样做。

答案 2 :(得分:0)

我不是这里的专家,但我会像你一样定期将许多tkinter小部件类子类化,并且我提供了两美分

您通常希望根据自己的偏好初始化任何Pet类属性,这就是您Pet.__init__的原因。此外,您可以通过将args直接传递给Pet

来改进代码
class Dog(Pet):
    def __init__(self, chases_cats, *args, **kwargs):
        Pet.__init__(self, *args, **kwargs)
        self.chases_cats = chases_cats

dog = Dog(chases_cats=True,name='max',species='Dog') 

if dog.chases_cats:
    print ("My %s, %s, enjoys chasing cats"%(dog.getSpecies(), dog.getName()))
#My Dog, max, enjoys chasing cats

答案 3 :(得分:0)

Python确实为您提供了处理base类实体的更大灵活性,面对覆盖,您仍然可以使用methods/attributes甚至基类来调用基类super

并非您坚持要求base.__init__,除非您想为范围定义不同的属性,否则不需要在这两个类中编写__init__。在这里,您已为范围添加了其他属性,例如chases_cats,并且您正尝试将attributes base纳入您的范围。

def __init__(self, name, chases_cats):
        Pet.__init__(self, name, "Dog")
        self.chases_cats = chases_cats

您甚至可以使用初始化基本属性来定义子范围,除了您对parent属性不感兴趣并覆盖它之外,它什么都没有。

def __init__(self, name, chases_cats):
    self.chases_cats = chases_cats

并且您并不是要在base初始值设定项中启动subclass属性,inherited scope可以在需要时初始化base属性的任何位置