Commençons par les décorateurs Python.
Un décorateur Python est une fonction qui aide à ajouter des fonctionnalités supplémentaires à une fonction déjà définie.
En Python, tout est un objet. Les fonctions en Python sont des objets de première classe, ce qui signifie qu'elles peuvent être référencées par une variable, ajoutées dans des listes, passées en tant qu'arguments à une autre fonction, etc.
Considérez le code suivant.
def decorator_func(fun):
def wrapper_func():
print("La fonction Wrapper a démarré")
fun()
print("Fonction donnée décorée")
# La fonction Wrapper ajoute quelque chose à la fonction passée et le décorateur
# renvoie la fonction Wrapper
return wrapper_func
def dire_au_revoir():
print("Au revoir!!")
dire_au_revoir = decorator_func(dire_au_revoir)
dire_au_revoir()
# Sortie :
# La fonction Wrapper a démarré
# Au revoir!!
# Fonction donnée décorée
Ici, nous pouvons dire que la fonction décoratrice a modifié notre fonction dire_au_revoir et a ajouté quelques lignes de code supplémentaires.
Syntaxe Python pour décorateur
def decorator_func(fun):
def wrapper_func():
print("La fonction Wrapper a démarré")
fun()
print("Fonction donnée décorée")
# La fonction Wrapper ajoute quelque chose à la fonction passée et le décorateur
# renvoie la fonction Wrapper
return wrapper_func
@decorator_func
def dire_au_revoir():
print("Au revoir!!")
dire_au_revoir()
Passons par tout cela avec un scénario. Mais avant cela, parlons de quelques principes de POO.
Les accesseurs et les mutateurs sont utilisés dans de nombreux langages de programmation orientée objet pour garantir le principe de l'encapsulation des données (qui est considéré comme l'intégration de données avec les méthodes qui opèrent sur ces données.)
Ces méthodes sont, bien sûr, l'accesseur pour récupérer les données et le mutateur pour changer les données.
Conformément à ce principe, les attributs d'une classe sont rendus privés pour les cacher et les protéger des autres codes.
Yup, @property est essentiellement un mode pythonique d'utiliser des accesseurs et des mutateurs.
Python a un excellent concept appelé propriété qui rend la vie d'un programmeur orienté objet beaucoup plus simple.
Supposons que vous décidiez de créer une classe qui pourrait stocker la température en degrés Celsius.
class Celsius:
def __init__(self, temperature = 0):
self.set_temperature(temperature)
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
def get_temperature(self):
return self._temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Température inférieure à -273 n'est pas possible")
self._temperature = value
Code refactorisé, voici comment nous aurions pu le réaliser avec 'property.'
En Python, property() est une fonction intégrée qui crée et renvoie un objet de propriété.
Un objet de propriété a trois méthodes, getter(), setter() et delete().
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
def get_temperature(self):
print("Obtenir la valeur")
return self.temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Température inférieure à -273 n'est pas possible")
print("Définir la valeur")
self.temperature = value
temperature = property(get_temperature,set_temperature)
Ici,
temperature = property(get_temperature,set_temperature)
aurait pu être décomposé comme suit,
# créer une propriété vide
temperature = property()
# assigner fget
temperature = temperature.getter(get_temperature)
# assigner fset
temperature = temperature.setter(set_temperature)
Point à noter :
- get_temperature reste une propriété au lieu d'une méthode.
Maintenant, vous pouvez accéder à la valeur de température en écrivant.
C = Celsius()
C.temperature
# au lieu d'écrire C.get_temperature()
Nous pouvons aller plus loin et ne pas définir les noms get_temperature et set_temperature car ils sont inutiles et polluent l'espace de noms de la classe.
La manière pythonique de traiter le problème ci-dessus est d'utiliser @property.
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
@property
def temperature(self):
print("Obtenir la valeur")
return self.temperature
@temperature.setter
def temperature(self, value):
if value < -273:
raise ValueError("Température inférieure à -273 n'est pas possible")
print("Définir la valeur")
self.temperature = value
Points à noter -
- Une méthode utilisée pour obtenir une valeur est décorée avec "@property".
- La méthode qui doit fonctionner comme le mutateur est décorée avec "@temperature.setter", Si la fonction avait été appelée "x", nous aurions dû la décorer avec "@x.setter".
- Nous avons écrit "deux" méthodes avec le même nom et un nombre différent de paramètres, "def temperature(self)" et "def temperature(self,x)".
Comme vous pouvez le voir, le code est certainement moins élégant.
Maintenant, parlons d'un scénario pratique de la vie réelle.
Disons que vous avez conçu une classe comme suit :
class NotreClasse:
def __init__(self, a):
self.x = a
y = NotreClasse(10)
print(y.x)
Supposons maintenant que notre classe soit devenue populaire auprès des clients et qu'ils ont commencé à l'utiliser dans leurs programmes, ils ont effectué toutes sortes d'affectations à l'objet.
Et un jour fatidique, un client de confiance est venu nous voir et a suggéré que "x" doit être une valeur entre 0 et 1000; c'est vraiment un scénario horrible!
Grâce aux propriétés, c'est facile : nous créons une version de propriété de "x".
class NotreClasse:
def __init__(self,x):
self.x = x
@property
def x(self):
return self.__x
@x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x = x
C'est génial, n'est-ce pas : Vous pouvez commencer avec l'implémentation la plus simple imaginable, et vous êtes libre de migrer plus tard vers une version de propriété sans avoir à changer l'interface! Donc les propriétés ne sont pas juste un remplacement pour les accesseurs et les mutateurs!
Vous pouvez vérifier cette implémentation ici
28 votes
Voir aussi: Comment fonctionnent les propriétés Python?
6 votes
property
est en fait une classe (pas une fonction), bien qu'elle appelle probablement la méthode__init__()
lorsque vous créez un objet, bien sûr. Utiliserhelp(property)
depuis le terminal est instructif.help
est également une classe pour une raison quelconque.4 votes
Je pense que ce lien offre un bon exemple : [propriété] (journaldev.com/14893/python-property-decorator)
9 votes
@Shule fil de discussion de 2 ans, mais toujours : Tout est une classe. Même les classes.
5 votes
Cela était aussi déroutant pour moi. J'ai finalement trouvé un article qui a pu simplifier les choses pour moi. J'espère que cela aidera quelqu'un d'autre. programiz.com/python-programming/property Je ne suis en aucun cas affilié au site.
0 votes
De nombreuses réponses dans cette discussion expliquent comment fonctionne
@property
, mais ne traitent pas pourquoi vous l'utiliseriez. La question "pourquoi utiliser@property
en Python" est l'équivalent de "pourquoi utiliser des accesseurs (getters/setters/deleters) en Java", car@property
est l'équivalent pythonique du même concept. Ces discussions complémentaires ont quelques discussions utiles stackoverflow.com/questions/1568091/… stackoverflow.com/questions/6618002/…0 votes
Sur l'utilité du décorateur de propriété : betterprogramming.pub/…
0 votes
ici un sujet connexe sur comment implémenter dynamiquement le protocole de descripteurs getter/setter
0 votes
J'ai une question liée à cette question. Dans l'exemple fourni, il y a un attribut protégé
self._x
, qui est protégé. Mais en utilisant la propriété définie ci-dessus, on peut définir et lire l'attribut en tapant simplementc = C(), c.x = 99
permettant ainsi à tous les programmeurs d'utiliser cet attribut comme s'il n'était PAS protégé. J'ai compris comment fonctionne @property. Mais je ne comprends toujours pas quel est l'avantage de l'utiliser dans l'exemple ci-dessus