Python: Превышение максимальной глубины рекурсии при вызове метода суперкласса из подкласса - PullRequest
1 голос
/ 03 июля 2011

У меня есть цепочка наследования из трех классов: Baz наследует от Bar, наследует от Foo.Я хотел бы определить метод в классе Foo, который при вызове в подклассе возвращает выходные данные своего родительского класса, к которому добавлены собственные элементы.Это желаемый результат:

>>> foo = Foo()
>>> bar = Bar()
>>> baz = Baz()
>>> print foo.get_defining_fields()
['in Foo']
>>> print bar.get_defining_fields()
['in Foo', 'in Bar']
>>> print baz.get_defining_fields()
['in Foo', 'in Bar', 'in Baz']

Проблема в том, что я неправильно понимаю что-то об использовании super() в методах суперкласса, вызываемых подклассами, или некоторых других деталях подкласса.Этот бит работает нормально:

>>> print foo.get_defining_fields()
['in Foo']

Но bar.get_defining_fields() создает бесконечный цикл, который запускается сам до тех пор, пока RuntimeError не будет поднят вместо вызова foo.get_defining_fields и остановится там, как я хочу.

Вот код.

class Foo(object):
    def _defining_fields(self):
        return ['in Foo']

    def get_defining_fields(self):
        if self.__class__ == Foo:
            # Top of the chain, don't call super()
            return self._defining_fields()
        return super(self.__class__, self).get_defining_fields() + self._defining_fields()

class Bar(Foo):
    def _defining_fields(self):
        return ['in Bar']

class Baz(Bar):
    def _defining_fields(self):
        return ['in Baz']

Итак, get_defining_fields определен в суперклассе, и вызов super() внутри него использует self.__class__ в попытке передать правильное имя подклассавызов в каждом подклассе.При вызове в Bar он разрешается до super(Bar, self).get_defining_fields(), так что список, возвращаемый foo.get_defining_fields(), будет добавлен к списку, возвращенному far.get_defining_fields().

Вероятно, простая ошибка, если вы правильно понимаете механику наследования Python ивнутреннюю работу super(), но так как я, очевидно, нет, я был бы признателен, если бы кто-то мог указать мне на правильный способ сделать это.


РЕДАКТИРОВАТЬ : согласно ответу Даниэля Роземана, я попытался заменить вызов super() на эту форму: return super(Foo, self).get_defining_fields() + self._defining_fields()

Теперь бесконечная рекурсия больше не происходит, но я получаю другую ошибку при вызове bar.get_defining_fields():

AttributeError: 'super' object has no attribute 'get_defining_fields'

Что-то еще все еще не в порядке.


РЕДАКТИРОВАТЬ : да, наконец-то разобрался, чего мне здесь не хватает.Отмеченный обновленный ответ Даниэля как принятый.

1 Ответ

6 голосов
/ 03 июля 2011

Проблема здесь:

 return super(self.__class__, self)...

self.__class__ всегда относится к текущему конкретному классу. Так что в Баре это относится к Бару, а в Базе это относится к Базу. Итак, Bar вызывает метод суперкласса, но self.__class__ по-прежнему относится к Bar, а не Foo. Таким образом, вы получите бесконечную рекурсию.

Вот почему вы всегда должны ссылаться на класс , в частности в супер. Как это:

return super(Foo, self)...

Редактировать Верно, потому что только верхний класс определяет get_defining_fields.

Вы идете об этом неправильно, правда. Это совсем не работа для super. Я думаю, что вы могли бы получить лучшие результаты, повторяя через self.__class__.__mro__, который является способом доступа к порядку разрешения методов суперклассов:

class Foo(object):
    def _defining_fields(self):
        return ['in Foo']

    def get_defining_fields(self):
        fields = []
        for cls in self.__class__.__mro__:
            if hasattr(cls, '_defining_fields'):
                fields.append(cls._defining_fields(self))
        return fields

¬

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