506 votes

Différence entre __getattr__ et __getattribute__

J'essaie de comprendre quand utiliser __getattr__ ou __getattribute__ . Le site documentation mentionne __getattribute__ s'applique aux classes de type nouveau. Qu'est-ce qu'une classe de type nouveau ?

2 votes

12 votes

@Trilarion pourtant cette question mentionne une réponse d'ici...

593voto

Ned Batchelder Points 128913

Une différence essentielle entre __getattr__ et __getattribute__ c'est que __getattr__ n'est invoqué que si l'attribut n'a pas été trouvé par les moyens habituels. C'est un bon moyen d'implémenter une solution de repli pour les attributs manquants, et c'est probablement celle que vous voulez.

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

Les classes de style nouveau dérivent de object Les classes à l'ancienne sont celles de Python 2.x qui n'ont pas de classe de base explicite. Mais la distinction entre les classes de l'ancien style et celles du nouveau style n'est pas la plus importante lorsqu'on choisit entre __getattr__ et __getattribute__ .

Vous voulez certainement __getattr__ .

7 votes

Puis-je les implémenter tous les deux dans la même classe ? Si c'est le cas, quel est l'intérêt d'implémenter les deux ?

15 votes

@Alcott : vous pouvez les mettre en œuvre tous les deux, mais je ne vois pas pourquoi vous le feriez. __getattribute__ sera appelé pour chaque accès, et __getattr__ sera appelé pour les fois où __getattribute__ a soulevé un AttributeError . Pourquoi ne pas tout garder en un seul endroit ?

12 votes

@NedBatchelder, si vous souhaitez remplacer (de manière conditionnelle) les appels à des méthodes existantes, il faut utiliser __getattribute__ .

198voto

N Randhawa Points 2888

Voyons quelques exemples simples des deux __getattr__ et __getattribute__ des méthodes magiques.

__getattr__

Python appellera __getattr__ chaque fois que vous demandez un attribut qui n'a pas encore été défini. Dans l'exemple suivant, ma classe Comte n'a pas __getattr__ méthode. Maintenant, dans main, lorsque j'essaie d'accéder aux deux obj1.mymin et obj1.mymax attributs tout fonctionne bien. Mais lorsque j'essaie d'accéder à obj1.mycurrent Python me donne AttributeError: 'Count' object has no attribute 'mycurrent'

class Count():
    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent)  --> AttributeError: 'Count' object has no attribute 'mycurrent'

Maintenant, ma classe Comte a __getattr__ méthode. Maintenant, lorsque j'essaie d'accéder à obj1.mycurrent python me renvoie ce que j'ai mis en œuvre dans mon __getattr__ méthode. Dans mon exemple, chaque fois que j'essaie d'appeler un attribut qui n'existe pas, python crée cet attribut et lui attribue la valeur entière 0.

class Count:
    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax    

    def __getattr__(self, item):
        self.__dict__[item]=0
        return 0

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent1)

__getattribute__

Voyons maintenant le __getattribute__ méthode. Si vous avez __getattribute__ dans votre classe, python invoque cette méthode pour chaque attribut, qu'il existe ou non. Alors pourquoi avons-nous besoin de __getattribute__ méthode ? Une bonne raison est que vous pouvez empêcher l'accès aux attributs et les rendre plus sûrs, comme le montre l'exemple suivant.

Chaque fois que quelqu'un essaie d'accéder à mes attributs qui commencent par la sous-chaîne "cur Leçons de python AttributeError exception. Sinon, il renvoie cet attribut.

class Count:

    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax
        self.current=None

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return object.__getattribute__(self,item) 
        # or you can use ---return super().__getattribute__(item)

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)

Important : Afin d'éviter une récursion infinie dans le programme __getattribute__ sa mise en œuvre doit toujours appeler la méthode de la classe de base portant le même nom pour accéder à tous les attributs dont elle a besoin. Par exemple : object.__getattribute__(self, name) ou super().__getattribute__(item) et non self.__dict__[item]

IMPORTANT

Si votre classe contient à la fois getattr et obtenir l'attribut méthodes magiques alors __getattribute__ est appelé en premier. Mais si __getattribute__ soulève AttributeError l'exception sera ignorée et __getattr__ sera invoquée. Voir l'exemple suivant :

class Count(object):

    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax
        self.current=None

    def __getattr__(self, item):
            self.__dict__[item]=0
            return 0

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return object.__getattribute__(self,item)
        # or you can use ---return super().__getattribute__(item)
        # note this class subclass object

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)

18voto

Simon K B Points 504

Il s'agit juste d'un exemple basé sur Explication de Ned Batchelder .

__getattr__ exemple :

class Foo(object):
    def __getattr__(self, attr):
        print "looking up", attr
        value = 42
        self.__dict__[attr] = value
        return value

f = Foo()
print f.x 
#output >>> looking up x 42

f.x = 3
print f.x 
#output >>> 3

print ('__getattr__ sets a default value if undefeined OR __getattr__ to define how to handle attributes that are not found')

Et si le même exemple est utilisé avec __getattribute__ Vous obtiendrez >>> RuntimeError: maximum recursion depth exceeded while calling a Python object

9 votes

En fait, c'est terrible. Le monde réel __getattr__() Les implémentations n'acceptent qu'un ensemble fini de noms d'attributs valides en soulevant AttributeError pour les noms d'attributs invalides, évitant ainsi problèmes subtils et difficiles à déboguer . Cet exemple accepte sans condition tous comme valides, ce qui constitue une mauvaise utilisation bizarre (et franchement source d'erreurs) de l'outil de gestion des noms d'attributs. __getattr__() . Si vous voulez un "contrôle total" sur la création des attributs comme dans cet exemple, vous voulez __getattribute__() à la place.

3 votes

@CecilCurry : Tous les problèmes que vous avez liés impliquent de retourner implicitement None plutôt qu'une valeur, ce que cette réponse ne fait pas. Qu'y a-t-il de mal à accepter tous les noms d'attributs ? C'est la même chose qu'un defaultdict .

3 votes

Le problème est que __getattr__ sera appelé avant recherche de superclasse. Ceci est acceptable pour une sous-classe directe de object Mais pour toute structure d'héritage plus complexe, vous supprimez complètement la possibilité d'hériter quoi que ce soit du parent.

15voto

sdolan Points 15572

Les classes de style nouveau héritent de object ou d'une autre nouvelle classe de style :

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

Les cours à l'ancienne ne le font pas :

class SomeObject:
    pass

Ceci ne s'applique qu'à Python 2 - dans Python 3, toutes les mesures ci-dessus créeront des classes de style nouveau.

Voir 9. Classes (tutoriel Python), NewClassVsClassicClass et Quelle est la différence entre les classes de style ancien et de style nouveau en Python ? pour les détails.

6voto

Mr Fooz Points 21092

Les classes de type nouveau sont celles qui sous-classent "objet" (directement ou indirectement). Elles ont un __new__ en plus de la méthode de la classe __init__ et ont un comportement un peu plus rationnel à bas niveau.

En général, vous voudrez remplacer __getattr__ (si vous surchargez l'un ou l'autre), sinon vous aurez du mal à supporter la syntaxe "self.foo" dans vos méthodes.

Informations supplémentaires : http://www.devx.com/opensource/Article/31482/0/page/4

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