使用 __init__ 继承属性

Posted

技术标签:

【中文标题】使用 __init__ 继承属性【英文标题】:The inheritance of attributes using __init__ 【发布时间】:2012-02-09 20:33:54 【问题描述】:

我是刚开始学习 Python 的 Java 人。举个例子:

class Person():
    def __init__(self, name, phone):
        self.name = name
        self.phone = phone

class Teenager(Person):
    def __init__(self, name, phone, website):
        self.name=name
        self.phone=phone
        self.website=website

我确定有很多冗余代码(我知道在 Java 中,上面的代码有很多冗余)。

对于已经从父类继承的属性,哪些部分是多余的?

【问题讨论】:

【参考方案1】:

到目前为止,所有示例都是针对 Python 2.x 的,但这里有一个针对 Python 3.x 的解决方案,它使用了较短版本的 super() 并且不从对象继承。

class Person:
    def __init__(self, name, phone):
        self.name = name
        self.phone = phone

class Teenager(Person):
    def __init__(self, name, phone, website):
        super().__init__(name, phone)
        self.website = website

【讨论】:

【参考方案2】:

在 python 中为类编写__init__ 函数时,应始终调用其超类的__init__ 函数。我们可以使用它来将相关属性直接传递给超类,因此您的代码将如下所示:

class Person(object):
    def __init__(self, name, phone):
        self.name = name
        self.phone = phone
class Teenager(Person):
    def __init__(self, name, phone, website):
        Person.__init__(self, name, phone)
        self.website=website

正如其他人指出的那样,您可以替换该行

Person.__init__(self, name, phone)

super(Teenager, self).__init__(name, phone)

代码也会做同样的事情。这是因为在 python 中instance.method(args) 只是Class.method(instance, args) 的简写。如果您想使用super,您需要确保将object 指定为Person 的基类,就像我在代码中所做的那样。

python documentation 包含有关如何使用 super 关键字的更多信息。在这种情况下,重要的是它告诉 python 在不是 Teenagerself 的超类中寻找方法 __init__

【讨论】:

请注意,如果您使用的是 Python 2.x,则必须将 object 显式列为 Person 的基类才能使用 super()。否则,您必须使用Person.__init__ 表单。 @chepner 你能提供一个参考吗?我找不到。 docs.python.org/library/functions.html#super 表示 super() 仅在新样式类上受支持,在 Python 2.x 中是继承自 object 的类 @chepner 谢谢。我没有意识到 python 类不会像在 Java 中那样自动从公共基类继承。我已经修复了我的代码。【参考方案3】:

__init__ 与 java 构造函数不同,不会为继承层次结构中的每个类自动调用 - 您需要自己做。

此外,您的所有对象层次结构都应植根于object(特殊情况除外)。

您的代码应为:

class Person(object):
    def __init__(self, name, phone):
        self.name = name
        self.phone = phone
class Teenager(Person):
    def_init_(self, name, phone, website):
        super(Teenager, self).__init__(name, phone)
        self.website=website

super 为其第二个参数 (self) 创建一个委托,它将调用函数列表中的下一个函数来调用每个名称(“方法解析顺序”)。这与调用超类方法并不完全相同,就像在 Java 中发生的那样。

你也可以写super(type(self), self).__init__(name, phone),但是如果你从这个类进一步继承,type(self)可能不是Teenager,你可能会有一个无限递归。这是您使用具有不同 MRO 的委托对象这一事实的一个实际结果,而不是直接调用超类构造函数。

【讨论】:

【参考方案4】:

Python 中的属性在构造函数中定义且不调用父类构造函数时不会被继承,除非您手动完成所有操作:

class Person():
    def __init__(self, name, phone):
        self.name = name
        self.phone = phone
class Teenager(Person):
    def_init_(self, name, phone, website):
        Person.__init__(self, name, phone)  # Call parent class constructor.
        self.website=website

更多信息在这里:http://docs.python.org/tutorial/classes.html#inheritance

【讨论】:

【参考方案5】:

我喜欢这样做的更简洁的方式:

class Teenager(Person):
        def __init__(self, *args, **kwargs):
           self.website=kwargs.pop('website')
           super(Teenager, self).__init__(*args, **kwargs)

在这种情况下并没有太大的区别,但是当您有一个带有大量参数的 __init__ 时,它会让生活变得更轻松。

【讨论】:

赞成使用 *args 和 **kwargs(...在有大量参数的情况下有效)。 使用argskwargs 通常会导致不一致的行为。从kwargs 中显式获取关键字意味着Teenager('a', 'b', 'c')不会正常工作。它以 KeyError 失败,因为在这种情况下 websiteargs[2]

以上是关于使用 __init__ 继承属性的主要内容,如果未能解决你的问题,请参考以下文章

python中的继承关系详解

9属性和函数的私有化,继承,重写

day24继承

继承类中的python __init__方法

面向对象之继承——python篇

推荐使用的派生方法:super().__init__()