D'une manière générale, un descripteur est un attribut d'objet avec un « comportement de liaison », et son contrôle d'accès est remplacé par la méthode du protocole de descripteur. Ces méthodes sont __get__(), __set__() et __delete__(). Les objets dotés de ces méthodes sont appelés descripteurs.
Le contrôle d'accès par défaut pour les attributs consiste à obtenir (obtenir), définir (définir) et supprimer (supprimer) du dictionnaire de l'objet (__dict__). Par exemple, l'ordre de recherche pour a.x est a.__dict__['x'], puis tapez(a).__dict__['x'], puis recherchez la classe parent de type(a) (à l'exclusion de la métaclasse). Si la valeur trouvée est un descripteur, Python appellera les méthodes du descripteur pour remplacer le comportement de contrôle par défaut. L'endroit où se produit ce remplacement dans la phase de recherche dépend de la méthode de descripteur définie. Notez que les descripteurs ne fonctionnent que dans une classe de nouveau style. Les classes de nouveau style et les classes de style ancien ont été mentionnées dans les chapitres précédents. Si vous êtes intéressé, vous pouvez consulter les chapitres précédents. La plus grande caractéristique des classes de nouveau style est que toutes les classes héritent de classes de type ou d'objet.
En programmation orientée objet, si les attributs d'une classe sont interdépendants, utiliser des descripteurs pour écrire du code peut organiser la logique de manière très astucieuse. Dans l'ORM de Django, des champs tels que InterField dans models.Model implémentent leurs fonctions via des descripteurs.
Regardons d'abord l'exemple suivant :
#!/usr/bin/env python3 # -*- coding: UTF-8 -*- class User(object): def __init__(self, name='两点水', sex='男'): self.sex = sex self.name = name def __get__(self, obj, objtype): print('获取 name 值') return self.name def __set__(self, obj, val): print('设置 name 值') self.name = val class MyClass(object): x = User('两点水', '男') y = 5 if __name__ == '__main__': m = MyClass() print(m.x) print('\n') m.x = '三点水' print(m.x) print('\n') print(m.x) print('\n') print(m.y)
Le résultat de sortie est le suivant :
获取 name 值 两点水 设置 name 值 获取 name 值 三点水 获取 name 值 三点水 5
A travers cet exemple, nous pouvons bien observer les appels de ces méthodes __get__() et __set__().
Regardez un autre exemple classique
Nous savons que la distance peut être exprimée en unités de « mètres » ou de « pieds ». Nous définissons maintenant une classe pour représenter la distance, qui a deux propriétés : les mètres et les pieds.
#!/usr/bin/env python3 # -*- coding: UTF-8 -*- class Meter(object): def __init__(self, value=0.0): self.value = float(value) def __get__(self, instance, owner): return self.value def __set__(self, instance, value): self.value = float(value) class Foot(object): def __get__(self, instance, owner): return instance.meter * 3.2808 def __set__(self, instance, value): instance.meter = float(value) / 3.2808 class Distance(object): meter = Meter() foot = Foot() if __name__ == '__main__': d = Distance() print(d.meter, d.foot) d.meter = 1 print(d.meter, d.foot) d.meter = 2 print(d.meter, d.foot)
Le résultat de sortie :
0.0 0.0 1.0 3.2808 2.0 6.5616
Dans l'exemple ci-dessus, avant d'attribuer une valeur à l'instance de Distance, nous pensons que le mètre et le pied devraient être des objets d'instance de leurs classes respectives, mais la sortie est une valeur numérique. C'est parce que __get__ entre en jeu.
Nous venons de modifier meter et de l'attribuer à int, mais foot a également été modifié. C'est là que __set__ entre en jeu.
L'objet descripteur (Meter, Foot) ne peut pas exister indépendamment, il doit être détenu par une autre classe propriétaire (Distance). L'objet descripteur peut accéder aux propriétés de son instance propriétaire, telles que instance.meter de Foot dans l'exemple.
section suivante