了解在子类中调用 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
,并且您正在尝试将 base
的 attributes
包含在您的范围中。
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
属性这是必需的。
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
,并且您正在尝试将 base
的 attributes
包含在您的范围中。
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
属性这是必需的。