使用 __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 在不是 Teenager
的 self
的超类中寻找方法 __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(...在有大量参数的情况下有效)。 使用args
和kwargs
通常会导致不一致的行为。从kwargs
中显式获取关键字意味着Teenager('a', 'b', 'c')
将不会正常工作。它以 KeyError 失败,因为在这种情况下 website
是 args[2]
。以上是关于使用 __init__ 继承属性的主要内容,如果未能解决你的问题,请参考以下文章