【问题标题】:Python, Overriding an inherited class methodPython,覆盖继承的类方法
【发布时间】:2012-09-27 17:11:22
【问题描述】:

我有两个班级,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(),Field 分配 a 和 b,然后将其中包含三个 0 的列表分配给 field。然后让 Background 的 init() 继续,然后调用它自己的 buildField() 并用包含 c 的列表覆盖 self.field。

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

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

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

【问题讨论】:

    标签: python inheritance methods super overwrite


    【解决方案1】:

    我希望调用后台 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 类中的方法。这是因为,这里的selfBackground 类的实例(尝试在__init__ 的@987654334 方法中打印self.__class__ @).. 当您在调用 __init__ 方法时传递它时,来自 Background 类..

    这就是你得到错误的原因..

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

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

    【讨论】:

      【解决方案2】:

      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() 实际上是在调用BackgroundbuildField(),这就是您收到该错误的原因。


      看来你的代码最好写成:

      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 )
      

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

      因此,如果您必须在构造函数中调用这些方法,最好使用classmethod decoratorstaticmethodbuildField() 分隔为属于该类。

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

      【讨论】:

        【解决方案3】:

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

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

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

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

        super 的问题在于它不会更改对象的类型,因此您不应更改超类的方法可能调用的任何方法的签名。

        【讨论】:

        • 您要完成的工作需要您在 Field.__init()__(self, a, b) 中输入 check self :它不是 Pythonic,但可以让您决定您想要的签名打电话。
        【解决方案4】:

        我希望调用后台 init()。将“a,b”传递给字段 init(), 分配 a 和 b 的字段

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

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

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

            self.field = self.buildField()
        

        即使这行出现在Field.__init__ 中,self 也是Background 的一个实例。所以self.buildField 找到BackgroundbuildField 方法,而不是Field 的方法。

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

        self.field = self.buildField()
        

        引发错误。


        那么我们如何告诉 Python 调用 FieldbuildField 方法而不是 Background 的方法?

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

        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__ 内部,

            self.field = self.__buildField()
        

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

            self.field = self.__buildField(c)
        

        Background.__init__ 内部调用Background__buildField 方法。

        【讨论】:

        • 使用“mangled”方法作为公共方法,我认为这不是一个好主意。
        • 这种情况下,有没有办法从后台调用Field的buildField方法?
        • 回答我自己的问题,用户super().buildField()
        【解决方案5】:

        Overriding 被谈论,但在我看来,chaining constructors or (methods)

        而且听起来也像 over-writing 属性:

        让我解释一下:

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

        • 然后,Background class 覆盖此属性。


        快速而肮脏的解决方案

        我不知道你的业务逻辑,但有时绕过超类的__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
        

        【讨论】:

          猜你喜欢
          • 2012-11-20
          • 1970-01-01
          • 1970-01-01
          • 2019-05-03
          • 1970-01-01
          • 2014-01-15
          • 2016-01-24
          • 1970-01-01
          • 2016-06-04
          相关资源
          最近更新 更多