Подкласс не инициализирует родительский класс в Python 2.x - PullRequest
0 голосов
/ 15 апреля 2019

У меня возникают некоторые проблемы с инициализацией родительского класса внутри одного подкласса в 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. Для меня это нелогично (и я бы сказал, что это даже ошибка), шаг [3] - [4] при установке атрибута .i_x класса Parent вызывает свойство класса Child.

Если переместить инициализацию родительского класса в конец дочернего класса или добавить отсутствующий атрибут как глобальный родительский класс (не для экземпляров), проблема исчезнет.

1 Ответ

0 голосов
/ 15 апреля 2019

Ваша проблема в том, что вы пытаетесь инициализировать несуществующие свойства / переменные класса (вы вызываете 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_grab из инициализации GfxObject(), ваш код заработал как шарм. По сути, я изменил этот класс только на этот:

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 decorator - если я не установил объект на None или какое-либо другое значение до того, как определил его как свойство, он бросил ошибка атрибута / не определено в первый раз, когда я попробую использовать этот метод получения / установки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...