92 votes

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

Je suis en train de modifier certaines de mes classes pour passer d'une utilisation extensive de getters et setters à une utilisation plus pythique des propriétés.

Mais maintenant je suis coincé parce que certains de mes getters ou setters précédents appelaient la méthode correspondante de la classe de base, puis effectuaient autre chose. Mais comment faire cela avec les propriétés ? Comment appeler le getter ou le setter de la propriété dans la classe mère ?

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

109voto

David Cournapeau Points 21956

Vous pourriez penser que vous pourriez appeler la fonction de la 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 car bar est une propriété, pas un callable.

Mais une propriété est juste 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)

0 votes

Pourquoi devrais-je appeler Foo.bar.fset(self, c) dans un setter hérité ? Pourquoi pas Foo.bar.fset(c) - sans le "soi". Je pensais que c'était implicitement transmis ?

2 votes

J'obtiens un TypeError : 'property' object is not callable.

0 votes

@nerdoc où se trouve le self obtenir des implications de la chaîne Foo.bar.fset ?

62voto

Pankrat Points 2145

Super devrait faire l'affaire :

return super().bar

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

return super(FooBar, self).bar

1 votes

TypeError : super() prend au moins 1 argument (0 donné)

4 votes

Je suppose (enfin, à en juger par le lien :P) que cette réponse est liée à python3. En python3, super() peut prendre zéro argument, oui.

0 votes

Super() fonctionne sans arguments en Python 3 et est équivalent à super(MyClass, self) mais plus lisible à mon avis.

28voto

Maxime R. Points 2199

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

Dans B, l'accès à la propriété getter de la classe parente A :

Comme d'autres 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 c'est suffisant pour étendre le getter.

Dans B, on accède à la propriété setter de la classe parente 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 devaient hériter d'un C qui étend également la propriété, vous n'aurez pas à mettre à jour la classe B Le code de l'entreprise.

Code complet de B prolongeant 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 :

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

4voto

workmad3 Points 12974

Essayez

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

Bien que je ne sois pas sûr que python supporte l'appel de la propriété de la classe de base. Une propriété est en fait un objet appelable qui est configuré avec la fonction spécifiée et remplace ensuite ce nom dans la classe. Cela pourrait facilement signifier qu'il n'y a pas de super fonction disponible.

Mais vous pouvez toujours modifier votre 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

0 votes

Cela fonctionne bien si vous écrivez la classe de base. Mais qu'en est-il si vous étendez une classe de base tierce qui utilise le même nom pour la propriété et le getter ?

3voto

Eric Points 36290

Quelques petites améliorations à La réponse de Maxime :

  • Utilisation de __class__ pour éviter d'écrire B . Notez que self.__class__ est le type d'exécution de self pero __class__ sans self est le nom de la définition de la classe englobante. super() est un raccourci pour super(__class__, self) .
  • Utilisation de __set__ au lieu de fset . Ce dernier est spécifique à property mais la première s'applique à tous les objets de type propriété (descripteurs).

    class B(A): @property def prop(self): value = super().prop

    do something with / modify value here

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X