52

我有两个班,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 )

此错误指向字段的buildField()

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

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

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

我期望像 c++ 这样一个类可以覆盖继承的方法的行为。我怎样才能做到这一点或类似的东西。

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

4

5 回答 5

52

我希望调用后台 init()。将 "a, b" 传递给 Fields init(),Field 以分配 a 和 b

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

然后将其中包含三个 0 的列表分配给字段。

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

    self.field = self.buildField()

即使这条线出现在Field.__init__,self也是 的一个实例Background。所以self.buildField找到Background'sbuildField方法,而不是Field's.

由于Background.buildField需要 2 个参数而不是 1 个参数,

self.field = self.buildField()

引发错误。


那么我们如何告诉 Python 调用Field'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__buildFieldinsideField所以 inside Field.__init__

    self.field = self.__buildField()

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

    self.field = self.__buildField(c)

内部Background.__init__调用Background__buildField方法。

于 2012-10-07T00:35:03.947 回答
29

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

首先,具有相同名称和不同签名的方法不会像在 C++ 中那样重载它。如果您的 Background 对象之一尝试在没有参数的情况下调用 buildField,则不会调用 Field 的原始版本——它已被完全隐藏。

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

Field__init__应该处理一个具有不带参数的 buildField 方法的对象。您将该方法与一个具有一个带有一个参数的 buildField 方法的对象一起使用。

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

于 2012-10-07T00:32:32.217 回答
18

我希望调用后台 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 方法.. 并传递selfas 参数.. 现在这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__在您的__init__方法中打印Field class)。当您在调用__init__方法时从Background类中传递它时。

这就是为什么你得到错误..

错误“TypeError: buildField() 正好需要 2 个参数(给定 1 个)。

因为你没有传递任何值。所以,只有传递的值是隐含的self

于 2012-10-07T00:17:54.673 回答
5

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()Background这就是您收到该错误的原因。


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

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 )

如果您不能让基础构造函数完成,则表明设计存在缺陷。

因此,如果必须在构造函数中调用这些方法,最好使用装饰器或来区分buildField()属于该类。classmethodstaticmethod

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

于 2012-10-07T00:31:03.033 回答
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
于 2016-03-27T12:58:46.197 回答