Como chamar uma propriedade da classe base se esta propriedade está sendo substituído na classe derivada?
-
06-07-2019 - |
Pergunta
Eu estou mudando algumas classes de meus de um extenso uso de getters e setters para um uso mais Python de propriedades.
Mas agora eu estou preso porque alguns dos meus getters ou setters anteriores iria chamar o método correspondente da classe base, e em seguida, executar outra coisa. Mas como isso pode ser conseguido com propriedades? Como chamar o getter propriedade ou setter na classe pai?
É claro que apenas chamando o próprio atributo dá recursão 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
Solução
Você pode pensar que você poderia chamar a função de classe base que é chamado por propriedade:
class FooBar(Foo):
@property
def bar(self):
# return the same value
# as in the base class
return Foo.bar(self)
Embora esta é a coisa mais óbvia a tentar Eu acho - Não funciona porque bar é uma propriedade, não um que pode ser chamado
.Mas a propriedade é apenas um objeto, com um método getter para encontrar o atributo correspondente:
class FooBar(Foo):
@property
def bar(self):
# return the same value
# as in the base class
return Foo.bar.fget(self)
Outras dicas
Super deve fazer o truque:
return super().bar
Em Python 2.x você precisa usar o mais detalhado sintaxe:
return super(FooBar, self).bar
Há uma alternativa usando super
que não requer para se referir explicitamente o nome da classe base.
Classe 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
Em B, acessando a propriedade getter da classe pai A:
Como outros já respondeu, é:
super(B, self).prop
Ou em Python 3:
super().prop
Isso retorna o valor retornado pelo getter da propriedade, não o próprio getter, mas é suficiente para estender o getter.
Em B, acessando o setter propriedade da classe pai A:
A melhor recomendação que eu tenho visto até agora é a seguinte:
A.prop.fset(self, value)
Eu acredito que este é o melhor:
super(B, self.__class__).prop.fset(self, value)
Neste exemplo ambas as opções são equivalentes, mas usando Super tem a vantagem de ser independente das classes de base de B
. Se B
foram para herdar de uma classe C
também estender a propriedade, você não teria de código de atualização do B
.
código completa de B estendendo a propriedade 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)
Uma ressalva:
A menos que sua propriedade não tem um setter, você tem que definir tanto o setter e getter em B
mesmo se você só mudar o comportamento de um deles.
try
@property
def bar:
return super(FooBar, self).bar
Embora eu não tenho certeza se suportes python chamando a propriedade classe base. A propriedade é realmente um objeto que pode ser chamado, que é configurado com a função especificada e, em seguida, substitui o nome da classe. Isto poderia facilmente significar que não há nenhuma função de super disponível.
Você pode sempre mudar a sua sintaxe para utilizar a função de propriedade () que:
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()
(que é a menos que eu estou faltando alguma coisa de sua explicação)