web-dev-qa-db-fra.com

Remplacement des propriétés dans python

Donc, j'essaie de trouver la meilleure façon (la plus élégante avec le moins de code) de permettre de remplacer des fonctions spécifiques d'une propriété (par exemple, juste le getter, juste le setter, etc.) en python. Je suis un fan de la façon suivante de faire des propriétés, car toutes leurs méthodes sont encapsulées dans le même bloc de code en retrait (il est plus facile de voir où s'arrêtent les fonctions traitant d'une propriété et les fonctions traitant de la propriété prochain début):

@apply
def foo():
    """A foobar"""
    def fget(self):
        return self._foo
    def fset(self, val):
        self._foo = val
    return property(**locals())

Cependant, si je veux hériter d'une classe qui définit les propriétés de cette manière, puis, par exemple, remplacer la fonction setter foo, cela semble difficile. J'ai fait quelques recherches et la plupart des réponses que j'ai trouvées ont consisté à définir des fonctions distinctes dans la classe de base (par exemple getFoo et setFoo), à créer explicitement une définition de propriété à partir d'elles ( par exemple foo = property(lambda x: x.getFoo(), lambda x, y: x.setFoo(y), lambda x: x.delFoo())), puis remplacez getFoo, setFoo et delFoo si nécessaire.

Je n'aime pas cette solution, car cela signifie que je dois définir des lambas pour chaque propriété, puis écrire chaque appel de fonction (avant, j'aurais pu faire property(**locals())). Je n'ai pas non plus l'encapsulation que j'avais à l'origine.

Idéalement, ce que j'aimerais pouvoir faire serait quelque chose comme ceci:

class A(object):
    def __init__(self):
        self.foo = 8
    @apply
    def foo():
        """A foobar"""
        def fget(self):
            return self._foo
        def fset(self, val):
            self._foo = val
        return property(**locals())

class ATimesTwo(A):
    @some_decorator
    def foo():
        def fset(self, val):
            self._foo = val * 2
        return something

Et puis la sortie ressemblerait à quelque chose comme:

>>> a = A()
>>> a.foo
8
>>> b = ATimesTwo()
>>> b.foo
16

Fondamentalement, ATimesTwo hérite de la fonction getter de A mais remplace la fonction setter. Quelqu'un connaît-il une façon de procéder (d'une manière qui ressemble à l'exemple ci-dessus)? À quoi ressemblerait la fonction some_decorator Et à quoi la fonction foo devrait-elle renvoyer?

41
Jessica Hamrick

Je suis sûr que vous avez déjà entendu cela, mais apply est obsolète depuis huit ans , depuis Python 2.3. Ne l'utilisez pas. Votre utilisation de locals() est également contraire au Zen de Python - explicite vaut mieux qu'implicite. Si vous avez vraiment comme l'indentation accrue, il n'est pas nécessaire de créer un objet jetable, faites simplement

if True:
    @property
    def foo(self):
        return self._foo
    @foo.setter
    def foo(self, val):
        self._foo = val

Ce qui n'abuse pas locals, utilise apply, nécessite la création d'un objet supplémentaire, ou a besoin ensuite d'une ligne avec foo = foo() ce qui rend plus difficile de voir la fin du bloc . Cela fonctionne aussi bien pour votre ancienne façon d'utiliser property - faites simplement foo = property(fget, fset) comme d'habitude.

Si vous souhaitez remplacer une propriété dans une sous-classe arbitraire, vous pouvez utiliser un recette comme celle-ci .

Si la sous-classe sait où la propriété a été définie, faites simplement:

class ATimesTwo(A):
    @A.foo.setter
    def foo(self, val):
        self._foo = val * 2
46
agf

Le Python docs sur le décorateur property suggère l'idiome suivant:

class C(object):
    def __init__(self):
        self._x = None
    @property
    def x(self):
        return self._x
    @x.setter
    def x(self, value):
        self._x = value
    @x.deleter
    def x(self):
        del self._x

Et puis les sous-classes peuvent remplacer un seul setter/getter comme ceci:

class C2(C):
    @C.x.getter
    def x(self):
        return self._x * -1

C'est un peu verruqueux car remplacer plusieurs méthodes semble vous obliger à faire quelque chose comme:

class C3(C):
    @C.x.getter
    def x(self):
        return self._x * -1
    # C3 now has an x property with a modified getter
    # so modify its setter rather than C.x's setter.
    @x.setter 
    def x(self, value):
        self._x = value * 2

Bien sûr, au moment où vous remplacez getter, setter et deleter, vous pouvez probablement simplement redéfinir la propriété pour C3.

51
stderr