我有两个班级,Field
和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 )
此错误指向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 ()将不同的变量传递给超类。没有涉及覆盖任何东西。
答案 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
decorator或staticmethod
将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 )
分隔为属于该类构造函数。
但是,如果您的基类构造函数没有从内部调用任何实例方法,则可以安全地覆盖此基类的任何方法。
答案 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