了解在子类中调用 parent __init__

Understanding calling parent __init__ in subclass

Python 的新手并且很好奇是否有人可以尽可能通俗地解释为什么会这样,当您创建子class 时,您仍然需要调用 parent 初始化函数?

假设您有一个像这样的 parent class:

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,它显然使用了parentclass中的一些功能。我的研究告诉我,我需要做这样的事情:

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

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

我确定在我的理解中我遗漏了一些明显的东西,但它只是不适合我。

What I'm asking is: why does it work that way? If you're inheriting from the parent class, doesn't it stand to reason that you're then inheriting everything from the parent class, including the __init__ ?

如果您要覆盖它,则不会。如果你的子类没有定义它自己的__init__,那么超类__init__确实被调用了。但是,如果子类确实定义了它自己的 __init__,那么您将覆盖它,因此 Python 不会对您如何或是否要调用超类实现做出假设。

这为您提供了额外的灵活性。如果自动调用超类__init__,则必须在子类__init__之前或之后统一调用。但由于它不会自动调用,您可以随时调用它。您可以在子类 __init__ 的开头、末尾、中间某处或根本不调用它。这意味着您的子类可以在超类方法起作用之前 "set up" 事情,或者在超类方法起作用之后 "tweak" 事情,或者两者都做,或者完全取代超类功能。

此外,如果超类实现是自动调用的,那么如果子类不接受与超类相同的参数,将不清楚该怎么办。通过显式超类调用,您的子类可以接受比其超类更多或更少的参数,并且可以自行决定在调用超类时传递什么。

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

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

我不是这方面的专家,但我像你一样定期做 class 许多 tkinter 小部件 classes,我提供我的两分钱

您通常希望根据自己的喜好初始化任何 Pet class 属性,这就是为什么您会这样做 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

Python 确实给了你更多的灵活性来处理 base class 实体,面对覆盖你仍然可以使用 [= 调用基础 methods/attributes 14=] 甚至基础 class.

不是你非要调用base.__init__,你不需要在两个class中都写__init__,除非你想为你的作用域定义不同的属性.在这里,您已经向您的范围添加了其他属性,例如 chases_cats,并且您正在尝试将 baseattributes 包含在您的范围中。

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

也不是说你必须在 subclass 初始化器中初始化你的 base 属性,在 inherited scope 中的任何地方你都可以初始化 base 属性这是必需的。