关于继承:python,重写继承的类方法

Python, Overriding an inherited class method

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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()

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

我希望先调用background init()。要将"a,b"传递给field s in it(),field将分配a和b,然后将一个包含三个0的列表分配给field。然后让background的init()继续,然后调用它自己的buildField()并用包含c的列表覆盖self.field。

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

我期望C++这样的行为,其中一个类可以重写继承的方法。我如何才能做到这一点或类似的事情。

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


I expected Background init() to be called. To pass"a, b" to Fields
init(), Field to assign a and b

到目前为止,一切都很好。

then to assign a list with three 0's
in it to field.

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

1
    self.field = self.buildField()

虽然这条线出现在Field.__init__内,但selfBackground的一个实例。因此,self.buildField找到了BackgroundbuildField方法,而不是Field的方法。

由于Background.buildField期望2个参数而不是1个,

1
self.field = self.buildField()

引发错误。

那么,我们如何告诉python调用FieldbuildField方法而不是Background方法呢?

名称管理(用双下划线命名属性)的目的是解决这个确切的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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)

方法名称__buildFieldField内"损坏"到_Field__buildField,因此在Field.__init__内,

1
    self.field = self.__buildField()

调用self._Field__buildField(),这是Field__buildField方法。同样地,

1
    self.field = self.__buildField(c)

Background.__init__内部调用Background__buildField方法。


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

首先,重写具有不同签名的方法不会像C++那样重载它。如果您的一个后台对象试图在不带参数的情况下调用buildField,则不会调用字段的原始版本——它已完全隐藏。

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

字段的__init__预期要处理的对象具有不带参数的buildField方法。该方法与一个对象一起使用,该对象的buildField方法采用一个参数。

super的特点是它不会改变对象的类型,所以不应该改变超类方法可能调用的任何方法的签名。


I expected Background init() to be called

实际上,Background init()正在被调用。

但是看看你的背景课……

1
2
3
4
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的参考。

现在在你的现场课上:

1
2
3
4
5
6
7
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()方法实际上正在调用后台类中的方法。这是因为,这里的selfBackground类的实例(尝试在Field class__init__方法中打印self.__class__)。当您在调用__init__方法时从Background类传递它。

这就是你出错的原因。

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

因为你没有传递任何值。因此,唯一传递的值是隐式self


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

1
2
3
4
def __init__( self, a, b ):
    self.a = a
    self.b = b
    self.field = self.buildField()

Field中。然而,这里的self指的是Background实例,而self.buildField()实际上是把BackgroundbuildField()称为BackgroundbuildField(),这就是你得到这个错误的原因。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class 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 )

如果您不能让基本构造函数完成,那么它就表示设计有缺陷。

因此,如果必须在构造函数中调用这些方法,那么最好使用classmethoddecorator或staticmethodbuildField()分离为属于该类。

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


说的是Overriding,但我听上去像是chaining constructors or (methods)

而且这听起来像是写得过火了:

让我解释一下:

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

  • 然后,Background类重写了这个属性。

快速而肮脏的解决方案

我不知道您的业务逻辑,但有时通过传递super class的__init__方法,我可以获得更多的控制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/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]

使用属性

有更清晰的语法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/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