Как вызвать свойство базового класса, если это свойство перезаписывается в производном классе? - PullRequest
78 голосов
/ 20 июня 2009

Я изменяю некоторые мои классы с широкого использования геттеров и сеттеров на более питонное использование свойств.

Но теперь я застрял, потому что некоторые из моих предыдущих методов получения или установки вызывали соответствующий метод базового класса, а затем выполняли что-то еще. Но как это можно сделать с помощью свойств? Как вызвать свойство getter или setter в родительском классе?

Конечно, просто вызов самого атрибута дает бесконечную рекурсию.

class Foo(object):

    @property
    def bar(self):
        return 5

    @bar.setter
    def bar(self, a):
        print a

class FooBar(Foo):

    @property
    def bar(self):
        # return the same value
        # as in the base class
        return self.bar # --> recursion!

    @bar.setter
    def bar(self, c):
        # perform the same action
        # as in the base class
        self.bar = c    # --> recursion!
        # then do something else
        print 'something else'

fb = FooBar()
fb.bar = 7

Ответы [ 5 ]

88 голосов
/ 20 июня 2009

Вы можете подумать, что можете вызвать функцию базового класса, которая вызывается свойством:

class FooBar(Foo):

    @property
    def bar(self):
        # return the same value
        # as in the base class
        return Foo.bar(self)

Хотя это самая очевидная вещь, которую стоит попробовать - она не работает, потому что bar является свойством, а не вызываемым. Но свойство - это просто объект, с методом получения, чтобы найти соответствующий атрибут:

class FooBar(Foo):

    @property
    def bar(self):
        # return the same value
        # as in the base class
        return Foo.bar.fget(self)
44 голосов
/ 20 июня 2009

Супер должен сделать трюк:

return super().bar

В Python 2.x вам нужно использовать более подробный синтаксис:

return super(FooBar, self).bar
18 голосов
/ 06 июня 2016

Существует альтернатива, использующая super, которая не требует явной ссылки на имя базового класса.

Базовый класс A:

class A(object):
    def __init__(self):
        self._prop = None

    @property
    def prop(self):
        return self._prop

    @prop.setter
    def prop(self, value):
        self._prop = value

class B(A):
    # we want to extend prop here
    pass

В B доступ к получателю свойства родительского класса A:

Как уже ответили другие, это:

super(B, self).prop

Или в Python 3:

super().prop

Возвращает значение, возвращаемое получателем свойства, а не самим получателем, но его достаточно для расширения получателя.

В B доступ к установщику свойств родительского класса A:

Лучшая рекомендация, которую я видел до сих пор:

A.prop.fset(self, value)

Я считаю, что этот лучше:

super(B, self.__class__).prop.fset(self, value)

В этом примере оба варианта эквивалентны, но преимущество super в том, что они независимы от базовых классов B. Если B наследуется от класса C, также расширяющего свойство, вам не нужно обновлять код B.

Полный код B, расширяющий свойство A:

class B(A):
    @property
    def prop(self):
        value = super(B, self).prop
        # do something with / modify value here
        return value

    @prop.setter
    def prop(self, value):
        # do something with / modify value here
        super(B, self.__class__).prop.fset(self, value)

Одна оговорка:

Если ваше свойство не имеет установщика, вы должны определить и установщик, и получатель в B, даже если вы измените поведение только одного из них.

4 голосов
/ 20 июня 2009

1001 * попробовать *

@property
def bar:
    return super(FooBar, self).bar

Хотя я не уверен, поддерживает ли python вызов свойства базового класса. Свойство на самом деле является вызываемым объектом, который устанавливается с помощью указанной функции, а затем заменяет это имя в классе. Это может легко означать, что суперфункция недоступна.

Вы всегда можете переключить свой синтаксис для использования функции property ():

class Foo(object):

    def _getbar(self):
        return 5

    def _setbar(self, a):
        print a

    bar = property(_getbar, _setbar)

class FooBar(Foo):

    def _getbar(self):
        # return the same value
        # as in the base class
        return super(FooBar, self)._getbar()

    def bar(self, c):
        super(FooBar, self)._setbar(c)
        print "Something else"

    bar = property(_getbar, _setbar)

fb = FooBar()
fb.bar = 7
0 голосов
/ 20 июня 2009
    class Base(object):
      def method(self):
        print "Base method was called"

    class Derived(Base):
      def method(self):
        super(Derived,self).method()
        print "Derived method was called"

    d = Derived()
    d.method()

(это если я что-то упускаю из вашего объяснения)

...