J'essaie de comprendre la différence entre __getattr__
et __getattribute__
, cependant, j'y échoue.
Le réponse à la question de débordement de pile Différence entre __getattr__
contre __getattribute__
dit:
__getattribute__
est appelé avant d'examiner les attributs réels de l'objet et peut donc être difficile à implémenter correctement. Vous pouvez très facilement vous retrouver dans des récursions infinies.
Je n'ai absolument aucune idée de ce que cela signifie.
Ensuite, il se poursuit:
Vous voulez presque certainement
__getattr__
.
Pourquoi?
J'ai lu que si __getattribute__
échoue, __getattr__
est appelé. Alors, pourquoi y a-t-il deux méthodes différentes qui font la même chose? Que dois-je utiliser si mon code implémente les nouvelles classes de style?
Je cherche des exemples de code pour résoudre cette question. J'ai googlé au mieux de mes capacités, mais les réponses que j'ai trouvées n'abordent pas le problème à fond.
S'il y a de la documentation, je suis prêt à le lire.
Avec les objets, vous devez gérer ses attributs. D'habitude nous faisons instance.attribute
. Parfois, nous avons besoin de plus de contrôle (lorsque nous ne connaissons pas le nom de l'attribut à l'avance).
Par exemple, instance.attribute
Deviendrait getattr(instance, attribute_name)
. En utilisant ce modèle, nous pouvons obtenir l’attribut en fournissant nom_attribut sous forme de chaîne.
__getattr__
Vous pouvez également indiquer à une classe comment gérer les attributs qu’elle ne gère pas explicitement, et ce, via la méthode __getattr__
.
Python appellera cette méthode chaque fois que vous demanderez un attribut qui n'a pas encore été défini, afin que vous puissiez définir quoi faire avec.
Un cas d'utilisation classique:
class A(dict):
def __getattr__(self, name):
return self[name]
a = A()
# Now a.somekey will give a['somekey']
__getattribute__
Si vous devez intercepter chaque attribut qu'il existe ou non, utilisez plutôt __getattribute__
. La différence est que __getattr__
N'est appelé que pour les attributs qui n'existent pas réellement. Si vous définissez directement un attribut, le référencement de cet attribut le récupérera sans appeler __getattr__
.
__getattribute__
Est appelé toutes les fois.
__getattribute__
Est appelée chaque fois qu'un accès d'attribut se produit.
class Foo(object):
def __init__(self, a):
self.a = 1
def __getattribute__(self, attr):
try:
return self.__dict__[attr]
except KeyError:
return 'default'
f = Foo(1)
f.a
Cela provoquera une récursion infinie. Le coupable ici est la ligne return self.__dict__[attr]
. Supposons (c'est assez proche de la vérité) que tous les attributs sont stockés dans self.__dict__
Et disponibles sous leur nom. La ligne
f.a
tente d'accéder à l'attribut a
de f
. Ceci appelle f.__getattribute__('a')
. __getattribute__
Tente ensuite de charger self.__dict__
. __dict__
Est un attribut de self == f
Et ainsi python appelle f.__getattribute__('__dict__')
) qui tente à nouveau d'accéder à l'attribut '__dict__
' C'est une récursion infinie.
Si __getattr__
Avait été utilisé à la place, alors
f
possède un attribut a
.f.b
), Il n'aurait pas été appelé pour trouver __dict__
Car il existe déjà et __getattr__
N'est invoqué que si - toutes les autres méthodes de recherche de l'attribut ont échoué.La manière correcte d’écrire la classe ci-dessus en utilisant __getattribute__
Est
class Foo(object):
# Same __init__
def __getattribute__(self, attr):
return super(Foo, self).__getattribute__(attr)
super(Foo, self).__getattribute__(attr)
lie la méthode __getattribute__
de la superclasse 'la plus proche' (formellement, la classe suivante dans l'ordre de résolution de la méthode ou MRO de la classe) à l'objet actuel self
, puis appelle et laisse faire le travail.
Tous ces problèmes sont évités en utilisant __getattr__
Qui laisse Python faites-le normalement jusqu'à ce qu'un attribut ne soit pas trouvé. À ce stade, = Python laisse le contrôle sur votre méthode __getattr__
Et lui permet de créer quelque chose.
Il convient également de noter que vous pouvez rencontrer une récursion infinie avec __getattr__
.
class Foo(object):
def __getattr__(self, attr):
return self.attr
Je vais laisser celui-ci comme un exercice.
Je pense que les autres réponses ont très bien expliqué la différence entre __getattr__
et __getattribute__
, mais une chose qui pourrait ne pas être claire est pourquoi vous voudriez utiliser __getattribute__
. La bonne chose à propos de __getattribute__
est que cela vous permet essentiellement de surcharger le point lorsque vous accédez à une classe. Cela vous permet de personnaliser le mode d'accès aux attributs à un niveau bas. Par exemple, supposons que je veuille définir une classe dans laquelle toutes les méthodes utilisant uniquement un argument self sont traitées comme des propriétés:
# prop.py
import inspect
class PropClass(object):
def __getattribute__(self, attr):
val = super(PropClass, self).__getattribute__(attr)
if callable(val):
argcount = len(inspect.getargspec(val).args)
# Account for self
if argcount == 1:
return val()
else:
return val
else:
return val
Et de l'interprète interactif:
>>> import prop
>>> class A(prop.PropClass):
... def f(self):
... return 1
...
>>> a = A()
>>> a.f
1
Bien sûr, c’est un exemple ridicule et vous ne voudrez probablement jamais faire cela, mais cela vous montre le pouvoir que vous pouvez obtenir en écrasant __getattribute__
.