Python继承类中的__init__方法

25

我想在不用显式调用新方法的情况下,给一个子类添加一些额外的属性。那么有没有一种方法可以给继承的类一个__init__类型的方法,而不覆盖父类的__init__方法?

我写了下面的代码,仅是为了阐明我的问题(因此属性命名不佳等)。

class initialclass():
    def __init__(self):
        self.attr1 = 'one'
        self.attr2 = 'two'    

class inheritedclass(initialclass):
    def __new__(self):
        self.attr3 = 'three'

    def somemethod(self):
        print 'the method'


a = inheritedclass()

for each in a.__dict__:
    print each

#I would like the output to be:
attr1
attr2
attr3

谢谢你


在派生类构造函数中调用initialclass.__init__。但是,attrX的顺序不能保证。 - khachik
1
除非你确切知道__new__的作用以及为什么需要修改它,否则请不要覆盖它。你可能并不知道它的真正含义。它不是__init__的别名。它比我们通常关注的魔法级别高三倍,尤其是对于初学者来说。 - user395760
请注意,__new__ 函数的参数是 selfclass 而不是 self 自身,因为它是一个隐式的类方法。所以在你的例子中,实际上你正在设置类属性 inheritedclass.attr3,而不是你认为的 self.attr3 - Lauritz V. Thaulow
6个回答

39
据我所知,这是不可能的,不过你可以调用超类的init方法,像这样:

class inheritedclass(initialclass):
    def __init__(self):
        initialclass.__init__(self)
        self.attr3 = 'three'

2
太棒了,我一直在尝试找到一个好的处理这种情况的方法。 - qwwqwwq
1
这个有什么缺点吗? - Mohsin

17

使用 super 调用父类的 __init__

class inheritedclass(initialclass):
    def __new__(self):
        self.attr3 = 'three'
        super(initialclass, self).__init__()

我强烈建议遵循Python的命名约定,以大写字母开头命名类,例如InheritedClassInitialClass。这有助于快速区分类和方法/变量。


2
具体来说,在Python中使用super(DerivingClass, self).__init__()或者仅仅是在Python 3中使用super().__init__()。编辑:由于1.super需要继承的类,2.您没有必要覆盖__new__,目前这段代码实际上是不正确的(它创建了一个属性attr3),因此扣1分。 - user395760
这个可以使用__new__方法,但是lazyr告诉我(正确地)不应该使用__new__,所以我认为对我来说正确的答案是yexo的。谢谢! - Anake
@Anake 给予应有的赞誉,是 @delnan 告诉你不要使用 __new__,我只是解释了你的代码实际上做了什么。顺便说一句,如果你实现了 __new__,它必须返回其类的实例。在上面的答案中,以及你的代码中,inheritedclass() is None!换句话说,它根本不起作用。(我错误地给了这个答案一个赞,没有正确阅读代码,现在我无法更改 :(。) - Lauritz V. Thaulow

6
首先,您混淆了__init____new__,它们是不同的东西__new__不接受实例(self)作为参数,而是接受类(cls)。
至于您问题的主要部分,您需要使用super来调用超类的__init__
您的代码应该像这样:
class initialclass(object):
    def __init__(self):
        self.attr1 = 'one'
        self.attr2 = 'two'    

class inheritedclass(initialclass):
    def __init__(self):
        self.attr3 = 'three'
        super(inheritedclass, self).__init__()

谢谢,我关于新方法犯了一个错误。我正在尝试使用super(),但是一直出现这个错误:TypeError: super()的第1个参数必须是类型,而不是classobj。(我也完全复制并粘贴了您的代码) - Anake
1
@Anake:initialclass需要继承objectclass initialclass(object)),否则它是旧式类,这只是过去的一个令人头痛的遗物,应该避免使用。 - user395760

4

非常简单。定义一个新的__init__方法,并在开头调用父类的__init__

# assuming a class Base, its __init__ takes one parameter x

class Derived(Base):
    def __init__(self, x, y):
        # whatever initialization is needed so we can say Derived is-a Base
        super(Derived, self).__init__(x)
        # now, add whatever makes Derived special - do your own initialization
        self.y = y

在Python 3中,你不必(也因为简单起见,可能不应该)明确继承自object或将类和self传递给super

0

如果存在的话,只需从父类的init中调用指定的方法:

class initialclass():
    def __init__(self):
        self.attr1 = 'one'
        self.attr2 = 'two'  
        if hasattr(self, 'init_subclass'):
            self.init_subclass()

class inheritedclass(initialclass):
    def init_subclass(self):
        self.attr3 = 'three'

1
虚拟-1。在父类中不需要做任何额外的工作,更不用说hasattr和额外的方法了。你只需简单地定义一个新的__init__函数,使用super调用父类的__init__函数即可。 - user395760
@delnan 我知道这一点,但问题是“有没有一种方法可以给继承类一个init类型的方法 _而不覆盖父类的init方法_”。我提供了这样一种方式。这样做的额外好处是为另一个子类方法调用 仅仅 子类的init提供入口。 - Lauritz V. Thaulow
1
这就是为什么它是虚拟的。我知道这是 OP 请求的内容,但是 OP 问的是“我该如何使用锤子来做这件事?”而答案是“你需要使用螺丝刀”。 - user395760
@delnan 我认为 OP 问的是,借用你的比喻,“当我不想使用螺丝刀时,如何使用锤子来完成这个任务?” 我假设重写包括扩展,因为在我看来,扩展方法只是一种重写方法——一种以某种方式调用父方法的方法。我错误地认为他知道如何扩展方法。顺便说一下,所有的 Python 方法都是虚拟的吗?我从来没有听说过有人谈论 Python 中的虚拟方法。 - Lauritz V. Thaulow
你关于Python方法是虚拟的说法是正确的,-1也是虚拟的 ;) - user395760

0
class initialclass:
        def __init__(self):
                self.attr1 = 'one'
                self.attr2 = 'two'
class inheritedclass(initialclass):
        def __init__(self):
                super().__init__()
                self.attr3 = 'three'
        def somemethod(self):
                print (self.attr1, self.attr2, self.attr3)
a=inheritedclass()
a.somemethod()

 1. List item

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接