【问题标题】:When a subclass calls super() with __init__(), where/what is the superclass's object?当子类使用 __init__() 调用 super() 时,超类的对象在哪里/是什么?
【发布时间】:2016-01-10 14:01:40
【问题描述】:

根据Learn Python The Hard Way ex44super()__init__一起使用的说明:

class Child(object): 
    def __init__(self, stuff):
        self.stuff = stuff
        super(Child, self).__init__()

这与上面的 Child.altered 示例几乎相同, 除了我在 __init__ 中设置一些变量之前 Parent 用它的Parent.__init__. 初始化

所以实例化Child 也会导致实例化Parent。但是这个实例化是什么/在哪里?给定:

c = Child()

Parent 对象在哪里/是什么?

【问题讨论】:

  • c Parent对象”。一个Child 对象是一个Parent 对象,这就是继承的重点。在Parent.__init__ 中,self 将引用cChild 实例,就像它在Child.__init__ 中所做的那样(或者,更确切地说,将分配给c 的实例一次所有__init__ s 已经跑了)。
  • 我将使用 cmets 表达两个个人意见:第一:LPTHW 可能非常可怕,我建议您至少使用一本辅助书籍或资源(例如 dive into pythonthe python tutorial 或 Python 3 OOP 2nd Ed by Dusty Phillips(最后一个不是免费的))。第二:不错的用户名。
  • @jonrsharpe so "Parent initialize with its Parent.__init__" 并不意味着创建了一个单独的对象(c 除外)?
  • 没错。正如我所解释的,您正在对新的 Child 对象调用 Parent__init__ 方法。 c = Child() 仅创建一个 Child 对象,而不是单独的 Parent 对象。
  • @Pyderman 还有什么不清楚的地方吗?

标签: python python-2.7 inheritance subclass


【解决方案1】:

super(Child, self) 为您提供self 指向的对象作为Child 父类的实例(即Parent)。

Child__init__ 方法中,你首先做一些事情(self.stuff = stuff)然后调用Parent__init__ 方法在你的Child 对象上 987654332@指向!

您没有显式实例化Parent 对象,而是因为继承,每个Child 都是 Parent

@timgeb 一个实际使用示例,或者以这种方式使用 super() 的理由,会很有帮助。

当然,让我们看一个简单的例子。假设您正在为邮政服务构建应用程序。该服务提供多种物品,例如包裹、信件或明信片。这些项目中的每一个都共享一些属性。为简单起见,假设它们共享接收者的名称(在现实世界的应用程序中,您可能需要更多属性,例如接收者的完整地址)。

>>> class MailItem(object):
...     def __init__(self, receiver_name):
...         self.receiver_name = receiver_name
... 
>>> m = MailItem('Pyderman')
>>> m.receiver_name
'Pyderman'

这似乎工作正常。然而,我们的MailItem 太不具体了。如果物品是包裹怎么办?那么我们当然会关心它的重量。另一方面,如果该项目是明信片,重量将无关紧要。让我们为名为@9​​87654338@ 的包创建一个子类,它继承自MailItem。每个Package 对象is-a MailItem,但它会存储有关其重量的额外信息。

>>> class Package(MailItem):
...     def __init__(self, receiver_name, weight):
...         # treat the package as a MailItem and call the MailItem initializier
...         super(Package, self).__init__(receiver_name)
...         # store extra information about the weight in some arbitrary unit
...         self.weight = weight

当我们实例化一个Package时,我们要做的第一件事就是调用MailItem的初始化器,因为每个Package也是一个MailItem。这不会创建另一个MailItem 对象,而是在我们的新Package 对象上调用MailItem__init__ 方法。在这个例子中,我们可以通过将self.receiver_name = receiver_name 行从MailItem 的初始化程序复制到Package 的初始化程序而不是使用super 来实现相同的效果,但这将是重复代码并且违背了不要-Repeat-Yourself 原则。

让我们实例化一个Package 来看看代码在起作用。

>>> p = Package('Pyderman', 200)
>>> p.receiver_name
'Pyderman'
>>> p.weight
200

如您所见,对象p 确实有一个receiver_name,因为Package 的初始化程序通过
super(Package, self).__init__(receiver_name) 调用MailItem 初始化程序。

冒着重复自己的风险,让一件事变得清晰: 我们只使用p = Package('Pyderman', 200) 创建一个Package 对象,而不是另一个MailItem 对象。每个Package 对象is-a MailItem,Python 会告诉你:

>>> isinstance(p, MailItem)
True

当然,每个Package也是一个Package

>>> isinstance(p, Package)
True

然而,并不是每个MailItem 都是一个 Package

>>> m = MailItem('Pyderman')
>>> isinstance(m, Package)
False

这样想:每条鳄鱼都是爬行动物,但并非每条爬行动物都是鳄鱼。

【讨论】:

  • 所以真的是为了避免在父母的__init__ 中复制代码吗?听起来很合理,但有点不必要(因为它只是一行代码)。
  • 难道不是(根据我对继承的理解)我们可以从子类中完全省略 __init__ 函数,因为它正在继承这一事实将负责初始化?见pastebin.com/JkkSNHKu
  • @Pyderman 关于您的第一条评论:对于这个基本的继承示例,是的,但请考虑父类的初始化程序可能有数百行。关于您的第二条评论:是的,如果您希望子类在__init__ 方法中与基类所做的完全相同,您可以省略子类中__init__ 的定义。在我们的例子中,我们必须在Package 中定义__init__,因为我们想做的事情比MailItem__init__ 方法中发生的更多(设置权重)。
  • 感谢您一直坚持这个问题,直到我弄清楚,现在就是这样。谢谢。
【解决方案2】:

不是实例化,初始化 - 仔细阅读你引用的段落,你会发现它只提到了后者。初始化(有时在历史上也称为构造)是将空实例构建为给定类型的可用对象的过程。

Parent.__init__ 产生一个可用的Parent 对象; Child.__init__ 在此基础上创建一个可用的 Child 对象。因此,每个Child 对象通过扩展也是一个有效的Parent 对象。

【讨论】:

    猜你喜欢
    • 2013-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-06
    • 2017-06-30
    • 1970-01-01
    • 1970-01-01
    • 2019-05-26
    相关资源
    最近更新 更多