Python,重写继承的类方法

时间:2012-10-07 00:11:09

标签: python inheritance methods super overwrite

我有两个班级,FieldBackground。他们看起来有点像这样:

class Field( object ):
    def __init__( self, a, b ):
        self.a = a
        self.b = b
        self.field = self.buildField()

    def buildField( self ):
        field = [0,0,0]
        return field

class Background( Field ):
    def __init__( self, a, b, c ):
        super(Background, self).__init__( a, b )
        self.field = self.buildField( c )

    def buildField( self, c ):
        field = [c]
        return field

a, b, c = 0, 1, 2
background = Background( a, b, c )

此错误指向Field的buildField()

"TypeError: buildField() takes exactly 2 arguments (1 given)."

我希望首先调用Background init ()。要将“a,b”传递给Fields init (),要将字段分配给a和b,然后将其中包含三个0的列表分配给字段。然后,对于Background的 init ()继续,然后调用它自己的buildField()并使用包含c的列表覆盖self.field。

似乎我并不完全理解super(),但是在查看网络上和周围的类似继承问题后,我无法找到解决问题的方法。

我期望像c ++这样的行为,其中类可以覆盖继承的方法。我怎样才能实现这个或类似的东西。

我发现与此相关的大多数问题都是使用双下划线的人。我使用super继承的经验是使用继承的类 init ()将不同的变量传递给超类。没有涉及覆盖任何东西。

5 个答案:

答案 0 :(得分:39)

  

我希望调用Background init()。将“a,b”传递给Fields   init(),Field指定a和b

到目前为止,非常好。

  

然后分配一个包含三个0的列表   在它的领域。

阿。这是我们得到错误的地方。

    self.field = self.buildField()

即使此行出现在Field.__init__中,self也是Background的一个实例。因此self.buildField找到Background的{​​{1}}方法,而不是buildField

由于Field需要2个参数而不是1,

Background.buildField

引发错误。


那么我们如何告诉Python调用self.field = self.buildField() 的{​​{1}}方法而不是Field

name mangling(用双下划线命名属性)的目的是解决这个问题。

buildField

方法名称Background被“绑定”到class Field(object): def __init__(self, a, b): self.a = a self.b = b self.field = self.__buildField() def __buildField(self): field = [0,0,0] return field class Background(Field): def __init__(self, a, b, c): super(Background, self).__init__(a, b) self.field = self.__buildField(c) def __buildField(self, c): field = [c] return field a, b, c = 0, 1, 2 background = Background(a, b, c) 内的__buildField,因此在_Field__buildField内,

Field

调用Field.__init__,这是 self.field = self.__buildField() 的{​​{1}}方法。同样,

self._Field__buildField()

Field内调用__buildField的{​​{1}}方法。

答案 1 :(得分:20)

从C ++的角度来看,这里可能存在两种误解。

首先,覆盖具有不同签名的方法不会像在C ++中一样重载它。如果您的一个Background对象尝试调用不带参数的buildField,则不会调用Field的原始版本 - 它已被完全隐藏。

第二个问题是,如果超类中定义的方法调用buildField,则将调用子类版本。在python中,所有方法都是动态绑定的,就像C ++ virtual方法一样。

Field __init__期望处理一个没有参数的buildField方法的对象。您将该方法与具有buildField方法的对象一起使用了一个参数。

super的问题是它不会改变对象的类型,因此你不应该改变超类'方法可能调用的任何方法的签名。

答案 2 :(得分:16)

  

我希望调用后台init()

实际上Background init()被称为..

看看你的背景课程。

class Background( Field ):
    def __init__( self, a, b, c ):
        super(Background, self).__init__( a, b )
        self.field = self.buildField( c )

因此,__init__的第一个语句正在调用super class(Field) init方法..并将self作为参数传递。现在这个self实际上是一个引用Background class ..

现在在您的Field类中: -

class Field( object ):
    def __init__( self, a, b ):

        print self.__class__  // Prints `<class '__main__.Background'>`
        self.a = a
        self.b = b
        self.field = self.buildField()

您的buildField()方法实际上正在调用Background类中的方法。这是因为self这里是Background类的实例(尝试打印self.__class__ in您__init__的{​​{1}}方法。当您从Field class类调用__init__方法时传递它时,

这就是您收到错误的原因..

  

错误“TypeError:buildField()只需要2个参数(1   给出)。

因为你没有传递任何值..所以,只传递的值是隐式Background

答案 3 :(得分:3)

super(Background, self).__init__( a, b )将调用:

def __init__( self, a, b ):
    self.a = a
    self.b = b
    self.field = self.buildField()
Field中的

。但是,此处self指的是background个实例,而self.buildField()实际上是buildField()的{​​{1}},这就是您收到错误的原因。


似乎您的代码应更好地编写为:

Background

如果您不能允许基础构造函数完成,那么它表示设计存在缺陷。

因此,如果必须在您的中调用这些方法,最好使用classmethod decoratorstaticmethodclass Field( object ): def __init__( self, a, b ): self.a = a self.b = b self.field = Field.buildField() @classmethod def buildField(cls): field = [0,0,0] return field class Background( Field ): def __init__( self, a, b, c ): super(Background, self).__init__(a, b) self.field = Background.buildField(c) @classmethod def buildField(cls,c): field = [c] return field a, b, c = 0, 1, 2 background = Background( a, b, c ) 分隔为属于该类构造函数。

但是,如果您的基类构造函数没有从内部调用任何实例方法,则可以安全地覆盖此基类的任何方法。

答案 4 :(得分:1)

Overriding已被讨论,但听起来像chaining constructors or (methods)

它听起来像覆盖属性:

让我解释一下:

  • 名为 field 的属性将初始化为[0,0,0]@property装饰器看起来更合适。

  • 然后,Background覆盖此属性。

快速而肮脏的解决方案

我不知道你的业务逻辑,但有时候通过超级的__init__方法给了我更多的控制权:

#!/usr/bin/env python

class Field( object ):
    def __init__( self, a, b ):
        self.a = a
        self.b = b
        self.field = self.buildField()

    def buildField( self ):
        field = [0,0,0]
        return field

class Background( Field ):
    def __init__( self, a, b, c ):
        # super(Background, self).__init__( a, b )
        # Unfortunately you should repeat or move initializing a and b
        # properties here
        self.a = a
        self.b = b

        self.field = self.buildField( c )

    def buildField( self, c ):
        # You can access super class methods 
        assert super(Background, self).buildField() == [0,0,0]
        field = [c]
        return field


a, b, c = 0, 1, 2
bg = Background(a,b,c)
assert bg.field == [2]

使用属性

语法更清晰。

#!/usr/bin/env python

class Field( object ):

    @property
    def field(self):
        return [0,0,0]


    def __init__( self, a, b ):
        self.a = a
        self.b = b


class Background( Field ):
    def __init__( self, a, b, c ):
        super(Background, self).__init__( a, b )
        self.c = c

        assert  (self.a, self.b, self.c) == (0,1,2)  # We assigned a and b in 
                                                   # super class's __init__ method

        assert super(Background, self).field == [0,0,0]
        assert self.field == [2]

    @property
    def field(self):
        return [self.c]


a, b, c = 0, 1, 2

background = Background( a, b, c )

print background.field