255 votes

Comprendre la différence entre __getattr__ et __getattribute__

Je suis en train d'essayer de comprendre la différence entre __getattr__ et __getattribute__, cependant, je suis en situation d'échec.

Cette question ici sur le dit,

__getattribute__ est invoquée avant de regarder les attributs réels sur l'objet, et donc peut être difficile de mettre en œuvre correctement. Vous pouvez vous retrouver dans infini récurrences très facilement.

Je n'ai absolument aucune idée de ce que cela signifie.

Puis il va dire:

Vous avez presque certainement souhaitez __getattr__.

Pourquoi?

J'ai lu que si __getattribute__ d'échec, __getattr__ est appelé. Alors pourquoi il y a deux manières différentes de faire la même chose? Si mon code met en œuvre les nouvelles classes de style, que dois-je utiliser?

Je suis à la recherche pour des exemples de code pour effacer ce doute. J'ai Googlé pour faire de mon mieux, mais les réponses que j'ai trouvé n'est pas de discuter du problème de fond.

Si il n'y a aucune documentation, je suis prêt à le lire.

365voto

pyfunc Points 31088

Quelques notions de base en premier:

Avec des objets, vous devez faire face à ses attributs. Ordinairement, nous n' instance.attribute

Parfois, nous avons besoin de plus de contrôle (lorsque nous ne savons pas le nom de l'attribut à l'avance)

instance.attribute deviendrait getattr(instance, attribute_name)

À l'aide de ce modèle, on peut récupérer l'attribut en fournissant le attribute_name. Cela fonctionne lorsque l'on donne un nom à la chaîne et que vous devez rechercher l'instance de l'attribut référencé par ce nom.

L'utilisation d' __getattr__

Vous pouvez aussi dire à une classe de la façon de traiter avec des attributs dont il n'a pas explicitement le gérer et de le faire via __getattr__ méthode.

Python va appeler cette méthode à chaque fois que vous demandez un attribut qui n'a pas déjà été définis. Ainsi, vous pouvez définir de quoi faire avec elle.

Un classique de cas d'utilisation:

class A(dict):
    def __getattr__(self, name):
       return self[name]
a = A()
# Now a.somekey will give a['somekey']

Mises en garde et de l'utilisation de __getattribute__

Si vous devez prendre chaque attribut regardless whether it exists or not, utilisez __getattribute__ à la place.

Différence

__getattr__ seulement est appelée pour les attributs qui n'existe pas réellement. Si vous définissez l'attribut directement référence à cet attribut va le récupérer sans appel __getattr__.

__getattribute__ est demandé à tous les temps.

116voto

aaronasterling Points 25749

__getattribute__ est appelé à chaque fois qu'un attribut d'accès 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

Ce sera la cause d'une récursion infinie. Le coupable est ici la ligne return self.__dict__[attr]. Imaginons (C'est assez proche de la vérité) que tous les attributs sont stockés dans self.__dict__ et disponible par leur nom. La ligne

f.a

les tentatives d'accès à l' a attribut de l' f. Cela exige f.__getattribute__('a'). __getattribute__ puis essaie de charger self.__dict__. __dict__ est un attribut de l' self == f et ainsi de python appels f.__getattribute__('__dict__') qui tente à nouveau d'accéder à l'attribut '__dict__'. C'est une récursion infinie.

Si __getattr__ a été utilisé à la place alors

  1. Elle n'aurait jamais exécuté, car f a a d'attribut.
  2. Si elle avait couru, (disons que vous avez demandé l' f.b), alors elle n'aurait pas été appelé à trouver __dict__ parce que c'est déjà là et l' __getattr__ n'est invoquée que si toutes les autres méthodes de trouver l'attribut ont échoué.

La "bonne" façon d'écrire la classe ci-dessus à l'aide de __getattribute__ est

class Foo(object):
    # Same __init__

    def __getattribute__(self, attr):
        return super(Foo, self).__getattribute__(attr)

super(Foo, self).__getattribute__(attr) lie l' __getattribute__ méthode du "plus proche" de la superclasse (officiellement, la prochaine classe dans la classe de la Méthode de Résolution de l'Ordre, ou MRO) à l'objet courant en self , puis l'appelle et permet que de faire le travail.

L'ensemble de ce problème est évité en utilisant __getattr__ qui permet de Python ne c'est chose tout à fait normale jusqu'à ce qu'un attribut n'est pas trouvé. À ce point, Python mains le contrôle de votre __getattr__ méthode et permet de venir avec quelque chose.

Il est également intéressant de noter que vous pouvez exécuter dans une récursion infinie avec __getattr__.

class Foo(object):
    def __getattr__(self, attr):
        return self.attr

Je vais laisser ça comme un exercice.

66voto

Jason Baker Points 56682

Je pense que les autres réponses ont fait du bon travail en expliquant la différence entre __getattr__ et __getattribute__, mais une chose qui pourrait ne pas être clair, c'est pourquoi vous pouvez utiliser __getattribute__. La chose cool à propos de __getattribute__ essentiellement vous permet de surcharge de la dot lors de l'accès à une classe. Cela vous permet de personnaliser la façon dont les attributs sont accessibles à un niveau faible. Par exemple, supposons que je veux définir une classe où toutes les méthodes qui ne font qu'une auto argument sont traités 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'interactif interprète:

>>> import prop
>>> class A(prop.PropClass):
...     def f(self):
...             return 1
... 
>>> a = A()
>>> a.f
1

Bien sûr c'est un exemple stupide et vous ne serait probablement pas besoin de cela, mais il vous montre la puissance que vous pouvez obtenir à partir d'primordial __getattribute__.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X