web-dev-qa-db-fra.com

Comment appeler une propriété de la classe de base si cette propriété est écrasée dans la classe dérivée?

Je change certaines de mes classes d'une utilisation extensive des getters et setters à une utilisation plus pythonique des propriétés.

Mais maintenant, je suis bloqué parce que certains de mes getters ou setters précédents appellent la méthode correspondante de la classe de base, puis effectuent autre chose. Mais comment cela peut-il être accompli avec des propriétés? Comment appeler le getter ou le setter de propriété dans la classe parent?

Bien sûr, le simple fait d'appeler l'attribut lui-même donne une récursion infinie.

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
79
UncleZeiv

Vous pourriez penser que vous pourriez appeler la fonction de classe de base qui est appelée par la propriété:

class FooBar(Foo):

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

Bien que ce soit la chose la plus évidente à essayer, je pense - cela ne fonctionne pas parce que la barre est une propriété, pas un appelable.

Mais une propriété n'est qu'un objet, avec une méthode getter pour trouver l'attribut correspondant:

class FooBar(Foo):

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

Super devrait faire l'affaire:

return super().bar

Dans Python 2.x, vous devez utiliser la syntaxe la plus verbeuse:

return super(FooBar, self).bar
44
Pankrat

Il existe une alternative utilisant super qui ne nécessite pas de référencer explicitement le nom de la classe de base.

Classe de 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, accéder au getter de propriété de la classe parent A:

Comme d'autres l'ont déjà répondu, c'est:

super(B, self).prop

Ou en Python 3:

super().prop

Cela renvoie la valeur renvoyée par le getter de la propriété, pas le getter lui-même mais il suffit d'étendre le getter.

En B, accéder au setter de propriétés de la classe parent A:

La meilleure recommandation que j'ai vue jusqu'à présent est la suivante:

A.prop.fset(self, value)

Je crois que celui-ci est meilleur:

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

Dans cet exemple, les deux options sont équivalentes mais l'utilisation de super a l'avantage d'être indépendante des classes de base de B. Si B devait hériter d'une classe C étendant également la propriété, vous n'auriez pas à mettre à jour le code de B.

Code complet de B étendant la propriété 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)

Une mise en garde:

Sauf si votre propriété n'a pas de setter, vous devez définir à la fois le setter et le getter dans B même si vous ne changez que le comportement de l'un d'eux.

18
Maxime R.

essayer

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

Bien que je ne sais pas si python prend en charge l'appel de la propriété de classe de base. Une propriété est en fait un objet appelable qui est configuré avec la fonction spécifiée, puis remplace ce nom dans la classe. Cela pourrait signifie facilement qu'il n'y a pas de super fonction disponible.

Cependant, vous pouvez toujours changer de syntaxe pour utiliser la fonction 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
4
workmad3