首页 > 解决方案 > 子类未在 Python 2.x 中初始化父类

问题描述

我在 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属性尚未创建,因为 Child 类的初始化仍在初始化 Parent 类。.i_x对我来说,当设置Parent 类的属性触发 Child 类的属性时,步骤 [3]-[4] 是违反直觉的(我会说它甚至是一个错误) 。

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

标签: pythonoopinheritance

解决方案


您的问题是您正在尝试使用方法调用初始化不存在的类属性/变量(例如,您正在调用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

编辑

有趣的是,一旦我移动i_xs_grabGfxObject()'s init 开始,您的代码就像一个魅力。基本上我只将该类更改为:

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定义为属性之前没有将其设置为或其他值,那么第一次我不会抛出任何属性/未定义错误d 尝试使用该 getter/setter。


推荐阅读