¿Cómo llamar a una propiedad de la clase base si esta propiedad se sobrescribe en la clase derivada?
-
06-07-2019 - |
Pregunta
Estoy cambiando algunas clases mías de un uso extenso de getters y setters a un uso más pitónico de propiedades.
Pero ahora estoy atascado porque algunos de mis captadores o establecedores anteriores llamarían al método correspondiente de la clase base y luego realizarían otra cosa. Pero, ¿cómo se puede lograr esto con las propiedades? ¿Cómo llamar al getter o setter de la propiedad en la clase padre?
Por supuesto, solo llamar al atributo en sí mismo da una recursión infinita.
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
Solución
Podría pensar que podría llamar a la función de clase base que se llama por propiedad:
class FooBar(Foo):
@property
def bar(self):
# return the same value
# as in the base class
return Foo.bar(self)
Aunque esto es lo más obvio, creo que lo creo: no funciona porque la barra es una propiedad, no es invocable.
Pero una propiedad es solo un objeto, con un método getter para encontrar el atributo correspondiente:
class FooBar(Foo):
@property
def bar(self):
# return the same value
# as in the base class
return Foo.bar.fget(self)
Otros consejos
Super debería hacer el truco:
return super().bar
En Python 2.x necesita usar la sintaxis más detallada:
return super(FooBar, self).bar
Existe una alternativa usando super
que no requiere hacer referencia explícita al nombre de la clase base.
Clase base 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
En B, accediendo al captador de propiedades de la clase padre A:
Como otros ya han respondido, es:
super(B, self).prop
O en Python 3:
super().prop
Esto devuelve el valor devuelto por el captador de la propiedad, no el captador en sí, pero es suficiente para extender el captador.
En B, accediendo al configurador de propiedades de la clase padre A:
La mejor recomendación que he visto hasta ahora es la siguiente:
A.prop.fset(self, value)
Creo que este es mejor:
super(B, self.__class__).prop.fset(self, value)
En este ejemplo, ambas opciones son equivalentes, pero usar super tiene la ventaja de ser independiente de las clases base de B
. Si B
heredara de una clase C
que también extendiera la propiedad, no tendría que actualizar el código de B
.
Código completo de B que extiende la propiedad de 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)
Una advertencia:
A menos que su propiedad no tenga un setter, debe definir tanto el setter como el getter en B
incluso si solo cambia el comportamiento de uno de ellos.
prueba
@property
def bar:
return super(FooBar, self).bar
Aunque no estoy seguro de si python admite llamar a la propiedad de clase base. Una propiedad es en realidad un objeto invocable que se configura con la función especificada y luego reemplaza ese nombre en la clase. Esto podría significar fácilmente que no hay una súper función disponible.
Siempre puedes cambiar tu sintaxis para usar la función 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
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()
(es decir, a menos que me falte algo de su explicación)