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.
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
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
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. .
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.
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
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
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
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.
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.
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')