accès aux attributs - troisième et dernier notebook
aka putting it all together:
où on essaye de réconcilier toutes ces façons d’accéder aux attributs: properties, __getattr__, et autres descriptors
la mécanique générale:¶
pour les accès en écriture, peu de changement par rapport à ce qu’on a vu jusqu’ici: on écrit toujours directement dans l’objet
pour les accès en lecture: la logique des accès - on dirait en termes savants le protocole d’accès aux attributs* - est codée dans la dunder
__getattribute__
accès en écriture: __setattr__¶
par rapport à la version simpliste qu’on a vue dans les premiers chapitres, il y a peu de différence concernant les accès en écriture
on écrit toujours directement dans l’objet
il existe toutefois un hook: on invoque si elle existe la dunder __setattr__
accès en lecture: __getattribute__¶
1er niveau de customisation light
en redéfinissant
__getattr__on peut fournir un mécanisme de fallback
2ème niveau de customisation deep
redéfinir
__getattribute__qui alors peut choisir
d’appeler ou pas
__getattr__et de contourner les descripteurs/properties
avertissement¶
si
__getattr__est relativement inoffensifpar contre redéfinir
__getattribute__doit être utilisé avec parcimonie
et a vite fait de vous sauter à la figure !
class WithGetAttr:
# seulement pour les attributs
# non trouvés
def __getattr__(self, attrname):
print("getattr with name {}".format(attrname))
return 10
gwa = WithGetAttr()
gwa.xgetattr with name x
10# ce qui n'empêche pas d'avoir aussi des attributs "normaux"
gwa.y = 20
print(gwa.y)20
# vous pouvez toujours écrire ce
# que vous voulez dans __dict__
# ou autre, avec ce code
# un attribut d'instance
# renvoie toujours 100
class WithGetAttribute:
# le point d'entrée pour la recherche
def __getattribute__(self, attrname):
return 100
# comme notre __getattribute__
# n'implémente pas la recherche
# des défauts via __getattr__,
# cette méthode en réalité
# est inutile ici
def __getattr__(self, attrname):
print("ne passera jamais par ici")
# inutile d'essayer d'implémenter un
# descriptor ou une property...
# en exercice ..
wgu = WithGetAttribute()
wgu.foo100# pas la peine d'essayer
wgu.bar = 100
wgu.bar100ça va très loin¶
# même __dict__ !
wgu.__dict__100subtilité de __getattribute__¶
n’est pas utilisée pour la recherche
des méthodes spéciales en
__*__exercice : pourquoi ?
accès en écriture¶
quand on écrit
inst.x = blablale défaut cette fois est d’écrire dans
self.__dict__['x']et redéfinissant
__setattr__on peut choisir une autre stratégiepour faire appel à la stratégie par défaut
object.__setattr_
écriture¶
évidemment
__setattr__ne peut pas faireinstance.name = blablani même
setattr(instance, 'name', blabla)sous peine de récursion infinie
class WithSetAttr:
def __setattr__(self, attrname, value):
print("setting {} to {}".format(attrname, value))
# appeler la façon par défaut
# de remplir un attribut
object.__setattr__(self, attrname, value)
wsa = WithSetAttr()
wsa.name = 'john'
wsa.namesetting name to john
'john'conclusion¶
lecture¶
point d’entrée
__getattribute__pas utile dans les utilisations courantes
descriptors, mais surtout properties
permettent de traiter un attribut à la fois
__getattr__, agit sur tous les attributsqui ne sont pas trouvés autrement
écriture¶
point d’entrée unique
__setattr__la disymétrie est liée au fait
que l’écriture est plus simple
on écrit toujours dans l’objet
et pas dans la classe ou super-classes
exercice¶
attr-dynamic-properties
exercice¶
attr-proxy
pour en savoir plus¶
la doc sur les descriptors (qui parle aussi des properties)
pour les hard-core: le code c derrière tout ça