Pourquoi ai-je besoin de la classe des descripteurs ?
Il vous donne un contrôle supplémentaire sur le fonctionnement des attributs. Si vous êtes habitué aux getters et setters en Java, par exemple, c'est la façon dont Python le fait. L'un des avantages est que, pour les utilisateurs, cela ressemble à un attribut (il n'y a pas de changement de syntaxe). Vous pouvez donc commencer par un attribut ordinaire et, lorsque vous avez besoin de faire quelque chose de plus sophistiqué, passer à un descripteur.
Un attribut est simplement une valeur mutable. Un descripteur vous permet d'exécuter du code arbitraire lors de la lecture ou de la définition (ou de la suppression) d'une valeur. On peut donc imaginer l'utiliser pour faire correspondre un attribut à un champ dans une base de données, par exemple - une sorte d'ORM.
Une autre utilisation pourrait consister à refuser d'accepter une nouvelle valeur en lançant une exception dans le champ __set__
- rendant effectivement l'"attribut" en lecture seule.
Qu'est-ce que instance
et owner
ici ? (en __get__
). Quel est le but de ces paramètres ?
C'est assez subtil (et c'est la raison pour laquelle j'écris une nouvelle réponse ici - j'ai trouvé cette question en me demandant la même chose et je n'ai pas trouvé la réponse existante très bonne).
Un descripteur est défini sur une classe, mais il est généralement appelé depuis une instance. Lorsqu'il est appelé depuis une instance, les deux instance
et owner
sont fixés (et vous pouvez travailler owner
de instance
donc cela semble un peu inutile). Mais lorsqu'il est appelé depuis une classe, seul owner
est défini - c'est pourquoi il est là.
Ceci n'est nécessaire que pour __get__
car c'est la seule qui peut être appelée sur une classe. Si vous définissez la valeur de la classe, vous définissez le descripteur lui-même. De même pour la suppression. C'est pourquoi le owner
n'est pas nécessaire ici.
Comment pourrais-je appeler/utiliser cet exemple ?
Eh bien, voici une astuce sympa qui utilise des classes similaires :
class Celsius:
def __get__(self, instance, owner):
return 5 * (instance.fahrenheit - 32) / 9
def __set__(self, instance, value):
instance.fahrenheit = 32 + 9 * value / 5
class Temperature:
celsius = Celsius()
def __init__(self, initial_f):
self.fahrenheit = initial_f
t = Temperature(212)
print(t.celsius)
t.celsius = 0
print(t.fahrenheit)
(J'utilise Python 3 ; pour Python 2 vous devez vous assurer que ces divisions sont / 5.0
et / 9.0
). Cela donne :
100.0
32.0
Il existe d'autres moyens, sans doute meilleurs, d'obtenir le même effet en Python (par exemple, si la température était une propriété, ce qui est le même mécanisme de base mais place toute la source dans la classe Température), mais cela montre ce qui peut être fait...