子类未在Python 2.x中初始化父类

时间:2019-04-15 11:36:19

标签: python oop inheritance

我在Python 2的一个子类中初始化父类时遇到一些问题。我想做的是用子类中的属性覆盖父类属性。

以某种方式,当我不在子类设置器中使用方法_update_rect(self)时(例如_set_grab(self, ps_value)_set_grab(self, ps_value)),一切都会按预期进行。但是,一旦我使用它,父类的初始化就会失败(未达到print '+++ END GfxObject initialisation'),并且我得到AttributeError: 'GfxRect' object has no attribute '_s_grab'

正如我在一开始所说的那样,代码对我来说看起来是正确的,而有问题的方法_update_rect仅使用其自身的真实属性,因此我不知道错误在哪里。我可以避免在子类中使用继承作为解决方法,但我真的很想了解问题所在。

在此先感谢您。

# Extra code to simplify test code
#=================================
class pygame:
    class Rect:
        def __init__(self, x, y, w, h):
            self.x = x
            self.y = y
            self.w = w
            self.h = h


# Classes
#========
class GfxObject(object):
    """
    Generic Graphical Object which is the parent class of all the sub-classes below.
    """
    def __init__(self):
        self.i_x = 0
        self.i_y = 0
        self.s_grab = 'nw'


class GfxRect(GfxObject):
    """
    Class to draw a rectangle.
    """
    def __init__(self):
        print '--- START GfxObject initialisation'
        super(GfxRect, self).__init__()
        print '+++ END GfxObject initialisation'

        self._i_x = 0
        self._s_grab = 'nw'
        self._o_rect = None

        print self

        self._update_rect()

    def __str__(self):
        return unicode(self).encode('utf8')

    def __unicode__(self):
        u_out = u'<GfxRect>\n'
        u_out += u'  .i_x:     %s\n' % self.i_x
        u_out += u'  ._i_x:    %s\n' % self._i_x
        u_out += u'  .s_grab:  %s\n' % self.s_grab
        u_out += u'  ._s_grab: %s\n' % self._s_grab
        return u_out

    def _get_grab(self):
        return self._s_grab

    def _get_x(self):
        return self._i_x

    def _set_grab(self, ps_value):
        self._s_grab = ps_value
        #self._update_rect()
        self._b_redraw = True

    def _set_x(self, i_value):
        self._i_x = i_value
        self._update_rect()
        self._b_redraw = True

    def _update_rect(self):
        """
        Method to update the pygame rectangle object.
        :return:
        """

        # [1/?] Calculating the deltas for (x,y) based on the grab position
        #------------------------------------------------------------------
        if self._s_grab == 'nw':
            i_dx = 0
        elif self._s_grab == 'n':
            i_dx = -800 / 2
        else:
            raise ValueError('Invalid grab value "%s"' % self._s_grab)

        # [2/?] Applying the deltas
        #--------------------------
        i_x = self._i_x + i_dx

        self._o_rect = pygame.Rect(i_x, 0, 800, 600)

    i_x = property(fget=_get_x, fset=_set_x)
    s_grab = property(fget=_get_grab, fset=_set_grab)


# Main code
#==========
if __name__ == '__main__':
    o_progbar = GfxRect()

更新:在内部属性之后将父类的初始化移到子类中似乎可以解决这个问题,对我来说这更奇怪。

之前(无效)

def __init__(self):
    print '--- START GfxObject initialisation'
    super(GfxRect, self).__init__()
    print '+++ END GfxObject initialisation'

    self._i_x = 0
    self._s_grab = 'nw'
    self._o_rect = None

    self._update_rect()

(工作后)

def __init__(self):
    self._i_x = 0
    self._s_grab = 'nw'
    self._o_rect = None

    print '--- START GfxObject initialisation'
    super(GfxRect, self).__init__()
    print '+++ END GfxObject initialisation'

    self._update_rect()

...但是似乎在后台发生了一些错误。如果将print 'child class "_update_rect" called'添加到_update_rect方法中,则在运行脚本时会得到以下输出:

--- START GfxObject initialisation
child class "_update_rect" called  <-- ERROR!?
child class "_update_rect" called  <-- ERROR!?
+++ END GfxObject initialisation
child class "_update_rect" called  <-- this is correct
...

这意味着父类在初始化时正在调用子方法!?

更新2:似乎是初始化子类时的工作流程。

[1] Child.__init__()
[2] Parent.__init__()
[3] self.i_x = 0
[4] Child._set_x(0)
[5] Child._update_rect()
[6] Child._s_grab = 'foo'

在步骤[6]中出现问题,因为自._s_grab属性尚未创建,因为子类的初始化仍在初始化父类。对我来说,设置父类的属性.i_x触发子类的属性时,步骤[3]-[4]是违反直觉的(而且我什至会说这甚至是个错误)。

通过在子类的末尾移动父类的初始化或将缺少的属性添加为全局的父类(不包括在实例中),问题消失了。

1 个答案:

答案 0 :(得分:0)

您的问题是,您正在尝试使用方法调用来初始化不存在的类属性/变量(例如,您正在调用self.i_x,但您已经初始化了self._i_x)。

将GfxObject更改为:

class GfxObject(object):
    """
    Generic Graphical Object which is the parent class of all the sub-classes below.
    """
    def __init__(self):
        self._i_x = 0
        self._i_y = 0
        self._s_grab = 'nw'

在我的机器上执行的脚本。结果输出:

$ python2 issue.py 
--- START GfxObject initialisation
+++ END GfxObject initialisation
<GfxRect>
  .i_x:     0
  ._i_x:    0
  .s_grab:  nw
  ._s_grab: nw

EDIT

有趣的是,一旦我从i_x的初始位置移走了s_grabGfxObject(),您的代码就成了一种魅力。基本上,我只将此类更改为:

class GfxObject(object):
    """
    Generic Graphical Object which is the parent class of all the sub-classes below.
    """
    i_x = None
    s_grab = None
    def __init__(self):
    #    self.i_x = None
    #    self.s_grab = None
        #self.s_grab = 'nw'
        pass

因此,似乎您遇到了与Python3和@property装饰器相同的问题-如果在将其定义为属性之前未将对象设置为None或其他某个值,那么在我第一次尝试使用该getter / setter时,它不会引发任何属性/未定义的错误。