Licence CC BY-NC-ND, Thierry Parmentelat & Arnaud Legout

from IPython.display import HTML
HTML(filename="_static/style.html")

attr.. (3/3) - __getattribute__#

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__

to review thoroughly

xxx

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 inoffensif

  • par 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.x
getattr 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.foo
100
# pas la peine d'essayer
wgu.bar = 100
wgu.bar
100

ça va très loin#

# même __dict__ !
wgu.__dict__
100

subtilité 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 = blabla

  • le défaut cette fois est d’écrire dans self.__dict__['x']

  • et redéfinissant __setattr__ on peut choisir une autre stratégie

  • pour faire appel à la stratégie par défaut

    • object.__setattr_

écriture#

  • évidemment __setattr__ ne peut pas faire

    • instance.name = blabla

    • ni 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.name
setting 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 attributs

    • qui 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#