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__
?这不是因为它是一个子类而自动传递给子类的吗?
我确定我在理解中遗漏了一些明显的东西,但它并没有点击我。
答案 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
属性的任何位置