web-dev-qa-db-fra.com

Exemple concret d'utilisation de la propriété property en python?

Je suis intéressé par comment utiliser @property en Python. J'ai lu les docs python et l'exemple à ce sujet, à mon avis, n'est qu'un code de jouet:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

Je ne sais pas quel (s) avantage (s) puis-je retirer de l'emballage du _x rempli avec le décorateur de la propriété. Pourquoi ne pas simplement implémenter en tant que:

class C(object):
    def __init__(self):
        self.x = None

Je pense que la fonctionnalité de propriété pourrait être utile dans certaines situations. Mais quand? Quelqu'un pourrait-il me donner des exemples concrets?

Merci.

132
xiao 啸

D'autres exemples seraient la validation/filtrage des attributs de l'ensemble (les forçant à être dans les limites ou acceptable) et l'évaluation paresseuse de termes complexes ou en évolution rapide.

Calcul complexe caché derrière un attribut:

class PDB_Calculator(object):
    ...
    @property
    def protein_folding_angle(self):
        # number crunching, remote server calls, etc
        # all results in an angle set in 'some_angle'
        # It could also reference a cache, remote or otherwise,
        # that holds the latest value for this angle
        return some_angle

>>> f = PDB_Calculator()
>>> angle = f.protein_folding_angle
>>> angle
44.33276

Validation:

class Pedometer(object)
    ...
    @property
    def stride_length(self):
        return self._stride_length

    @stride_length.setter
    def stride_length(self, value):
        if value > 10:
            raise ValueError("This pedometer is based on the human stride - a stride length above 10m is not supported")
        else:
            self._stride_length = value
88
benosteen

Un cas d'utilisation simple consiste à définir un attribut d'instance en lecture seule, comme vous le savez qui mène un nom de variable avec un trait de soulignement _x in python signifie généralement que c'est privé (usage interne) mais nous voulons parfois pouvoir lire l'attribut d'instance et ne pas l'écrire pour pouvoir utiliser property pour cela:

>>> class C(object):

        def __init__(self, x):
            self._x = x

        @property
        def x(self):
            return self._x

>>> c = C(1)
>>> c.x
1
>>> c.x = 2
AttributeError        Traceback (most recent call last)

AttributeError: can't set attribute
74
mouad

Jetez un oeil à cet article pour une utilisation très pratique. En bref, il explique comment Python vous pouvez généralement abandonner la méthode explicite d’obtention/définition, car si vous en avez besoin à un moment donné, vous pouvez utiliser property pour une implémentation transparente. .

20
Eli Bendersky

Une chose pour laquelle je l’ai utilisée est la mise en cache de valeurs stockées dans une base de données, qui sont lentes mais qui ne changent pas. Ceci se généralise à toute situation dans laquelle vos attributs nécessitent un calcul ou une autre opération longue (par exemple, un contrôle de base de données, une communication réseau) que vous ne souhaitez effectuer que sur demande.

class Model(object):

  def get_a(self):
    if not hasattr(self, "_a"):
      self._a = self.db.lookup("a")
    return self._a

  a = property(get_a)

C’était dans une application Web où une page donnée n’aurait besoin que d’un seul attribut de ce type, mais les objets sous-jacents eux-mêmes pourraient en avoir plusieurs. les attributs sont paresseux et qui ne le sont pas.

15
detly

La propriété est simplement une abstraction autour d'un champ qui vous donne plus de contrôle sur les manières de manipuler un champ spécifique et d'effectuer des calculs de middleware. Peu d’utilisations nous viennent à l’esprit sont la validation, l’initialisation préalable et la restriction d’accès

@property
def x(self):
    """I'm the 'x' property."""
    if self._x is None:
        self._x = Foo()

    return self._x
7
Stanislav Ageev

En lisant les réponses et les commentaires, le thème principal semble être que les réponses semblent manquer d'un exemple simple mais utile. J'en ai inclus un très simple ici qui démontre l'utilisation simple du @property décorateur. C’est une classe qui permet à un utilisateur de spécifier et d’obtenir une mesure de distance en utilisant différentes unités, c.-à-d. in_feet ou in_metres.

class Distance(object):
    def __init__(self):
        # This private attribute will store the distance in metres
        # All units provided using setters will be converted before
        # being stored
        self._distance = 0.0

    @property
    def in_metres(self):
        return self._distance

    @in_metres.setter
    def in_metres(self, val):
        try:
            self._distance = float(val)
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_feet(self):
        return self._distance * 3.2808399

    @in_feet.setter
    def in_feet(self, val):
        try:
            self._distance = float(val) / 3.2808399
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_parsecs(self):
        return self._distance * 3.24078e-17

    @in_parsecs.setter
    def in_parsecs(self, val):
        try:
            self._distance = float(val) / 3.24078e-17
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

Usage:

>>> distance = Distance()
>>> distance.in_metres = 1000.0
>>> distance.in_metres
1000.0
>>> distance.in_feet
3280.8399
>>> distance.in_parsecs
3.24078e-14
6
Mike

une autre fonctionnalité intéressante des propriétés sur l'utilisation des setters et getters est qu'elles vous permettent de continuer à utiliser des opérateurs OP = (par exemple + =, - =, * =, etc.) sur vos attributs tout en conservant toute validation, contrôle d'accès, mise en cache, etc. les setters et les getters fourniraient.

par exemple, si vous avez écrit la classe Person avec un setter setage(newage) et un getter getage(), vous devez alors incrémenter l'âge que vous devez écrire:

bob = Person('Robert', 25)
bob.setage(bob.getage() + 1)

mais si vous faites de age une propriété, vous pouvez écrire le beaucoup plus propre:

bob.age += 1
6
quizdog

Oui, pour l'exemple original posté, la propriété fonctionnera exactement comme si vous aviez simplement une variable d'instance 'x'.

C'est la meilleure chose à propos des propriétés de python. De l'extérieur, elles fonctionnent exactement comme des variables d'instance! Ce qui vous permet d'utiliser des variables d'instance de l'extérieur de la classe.

Cela signifie que votre premier exemple pourrait en réalité utiliser une variable d'instance. Si les choses changent et que vous décidez alors de modifier votre implémentation et qu'une propriété est utile, l'interface avec la propriété sera toujours la même à partir du code en dehors de la classe. n changement de variable d'instance en propriété n'a aucun impact sur le code en dehors de la classe.

De nombreux autres langages et cours de programmation indiqueront qu’un programmeur ne doit jamais exposer les variables d’instance, mais utilise plutôt des "getters" et des "setters" pour toute valeur accessible de l’extérieur de la classe, même le cas simple cité dans la question.

Code en dehors de la classe avec de nombreux langages (par exemple, Java), utilisez

object.get_i()
    #and
object.set_i(value)

#in place of (with python)
object.i
    #and 
object.i = value

Et lors de l'implémentation de la classe, il existe de nombreux "getters" et "setters" qui font exactement comme votre premier exemple: répliquer une variable d'instance simple. Ces getters et ces setters sont obligatoires car si l'implémentation de la classe change, tout le code en dehors de la classe devra être modifié. Mais les propriétés python permettent au code en dehors de la classe d'être identique à celui des variables d'instance. Il n'est donc pas nécessaire de modifier le code en dehors de la classe si vous ajoutez une propriété ou une variable d'instance simple. Donc, contrairement à la plupart des langues orientées objet, pour votre exemple simple, vous ( pouvez utiliser la variable d'instance au lieu de 'getters' et 'setters 'qui ne sont vraiment pas nécessaires, sachant que si vous changez de propriété, le code utilisant votre classe n’a pas besoin de changer.

Cela signifie que vous n'avez besoin de créer des propriétés que s'il existe un comportement complexe. Dans le cas simple très courant où, comme décrit dans la question, une simple variable d'instance suffit, vous pouvez simplement utiliser la variable d'instance.

5
innov8

La réponse courte à votre question est que, dans votre exemple, il n'y a aucun avantage. Vous devriez probablement utiliser le formulaire qui n'implique pas de propriétés.

La raison pour laquelle les propriétés existent, c’est que si votre code change à l’avenir et que vous devez soudainement utiliser davantage vos données: valeurs de cache, protection des accès, interrogation de ressources externes ... peu importe, vous pouvez facilement modifier votre classe pour ajouter des getters. et setters pour les données sans changer l'interface, de sorte que vous n'avez pas à trouver partout dans votre code où ces données sont accédées et à les modifier aussi.

5
SpoonMeiser

Ce que beaucoup ne remarquent pas au début, c’est que vous pouvez créer vos propres sous-classes de biens. J'ai trouvé cela très utile pour exposer des attributs d'objet en lecture seule ou des attributs que vous pouvez lire et écrire sans les supprimer. C'est également un excellent moyen d'encapsuler des fonctionnalités telles que le suivi des modifications apportées aux champs d'objet.

class reader(property):
    def __init__(self, varname):
        _reader = lambda obj: getattr(obj, varname)
        super(reader, self).__init__(_reader)

class accessor(property):
    def __init__(self, varname, set_validation=None):
        _reader = lambda obj: getattr(obj, varname)
        def _writer(obj, value):
            if set_validation is not None:
               if set_validation(value):
                  setattr(obj, varname, value)
        super(accessor, self).__init__(_reader, _writer)

#example
class MyClass(object):
   def __init__(self):
     self._attr = None

   attr = reader('_attr')
4
user1969453