J'essaie de comprendre quand utiliser __getattr__
ou __getattribute__
. La documentation mentionnée __getattribute__
s'applique aux classes de nouveau style. Que sont les classes de nouveau style?
python
getattr
getattribute
Yarin
la source
la source
Réponses:
Une différence clé entre
__getattr__
et__getattribute__
est qu'elle__getattr__
n'est invoquée que si l'attribut n'a pas été trouvé de la manière habituelle. C'est bon pour implémenter un repli pour les attributs manquants, et c'est probablement l'un des deux que vous voulez.__getattribute__
est invoqué avant de regarder les attributs réels de l'objet et peut donc être difficile à implémenter correctement. Vous pouvez vous retrouver très facilement dans des récursions infinies.Les classes de style nouveau dérivent de
object
, les classes de style ancien sont celles de Python 2.x sans classe de base explicite. Mais la distinction entre les classes de style ancien et nouveau n'est pas la plus importante lors du choix entre__getattr__
et__getattribute__
.Vous le voulez presque certainement
__getattr__
.la source
__getattribute__
sera appelé pour chaque accès, et__getattr__
sera appelé pour les temps qui ont__getattribute__
soulevé unAttributeError
. Pourquoi ne pas tout garder en un?__getattribute__
.objec.__getattribute__
invoquemyclass.__getattr__
dans les bonnes circonstances.Voyons quelques exemples simples des deux
__getattr__
et__getattribute__
des méthodes magiques.__getattr__
Python appellera la
__getattr__
méthode chaque fois que vous demandez un attribut qui n'a pas déjà été défini. Dans l'exemple suivant, ma classe Count n'a pas de__getattr__
méthode. Maintenant, lorsque j'essaie d'accéder aux deuxobj1.mymin
et auxobj1.mymax
attributs, tout fonctionne correctement. Mais quand j'essaie d'accéder à l'obj1.mycurrent
attribut - Python me donneAttributeError: 'Count' object has no attribute 'mycurrent'
Maintenant, ma classe Count a une
__getattr__
méthode. Maintenant, quand j'essaie d'accéder à l'obj1.mycurrent
attribut - python me renvoie tout ce que j'ai implémenté dans ma__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 le définit sur la valeur entière 0.__getattribute__
Voyons maintenant la
__getattribute__
méthode. Si vous avez une__getattribute__
méthode dans votre classe, python invoque cette méthode pour chaque attribut, qu'il existe ou non. Alors pourquoi avons-nous besoin d'une__getattribute__
méthode? Une bonne raison est que vous pouvez empêcher l'accès aux attributs et les rendre plus sécurisés, comme illustré dans l'exemple suivant.Chaque fois que quelqu'un essaie d'accéder à mes attributs qui commencent par la sous-chaîne 'cur', le python déclenche une
AttributeError
exception. Sinon, il renvoie cet attribut.Important: Afin d'éviter une récursion infinie dans la
__getattribute__
méthode, son implémentation doit toujours appeler la méthode de classe de base avec le même nom pour accéder aux attributs dont elle a besoin. Par exemple:object.__getattribute__(self, name)
ousuper().__getattribute__(item)
et nonself.__dict__[item]
IMPORTANT
Si votre classe contient à la fois des méthodes magiques getattr et getattribute , elle
__getattribute__
est appelée en premier. Mais si__getattribute__
lève uneAttributeError
exception, l'exception sera ignorée et la__getattr__
méthode sera invoquée. Voir l'exemple suivant:la source
__getattribute__
mais ce n'est sûrement pas le cas. Parce que selon votre exemple, tout ce que vous faites__getattribute__
est de lever l'AttributeError
exception si l'attribut n'est pas là dans__dict__
l'objet; mais vous n'en avez pas vraiment besoin car c'est l'implémentation par défaut de__getattribute__
et en fait__getattr__
c'est exactement ce dont vous avez besoin en tant que mécanisme de secours.current
est défini sur les instances deCount
(voir__init__
), donc simplement augmenterAttributeError
si l'attribut n'est pas là n'est pas tout à fait ce qui se passe - il diffère__getattr__
pour tous les noms commençant par 'cur', y compriscurrent
, mais aussicurious
,curly
...Ceci est juste un exemple basé sur l'explication de Ned Batchelder .
__getattr__
exemple:Et si le même exemple est utilisé avec
__getattribute__
Vous obtiendrez >>>RuntimeError: maximum recursion depth exceeded while calling a Python object
la source
__getattr__()
implémentations réelles n'acceptent qu'un ensemble fini de noms d'attributs valides en augmentantAttributeError
les noms d'attributs invalides, évitant ainsi les problèmes subtils et difficiles à déboguer . Cet exemple accepte inconditionnellement tous les noms d'attribut comme valides - une mauvaise utilisation bizarre (et franchement sujette aux erreurs) de__getattr__()
. Si vous voulez un "contrôle total" sur la création d'attributs comme dans cet exemple, vous le souhaitez__getattribute__()
.defaultdict
.__getattr__
sera appelé avant la recherche de superclasse. C'est OK pour une sous-classe directe deobject
, car les seules méthodes qui vous intéressent vraiment sont des méthodes magiques qui ignorent l'instance de toute façon, mais pour toute structure d'héritage plus complexe, vous supprimez complètement la possibilité d'hériter de quoi que ce soit du parent.Les classes
object
de nouveau style héritent de ou d'une autre nouvelle classe de style:Les classes à l'ancienne ne:
Cela ne s'applique qu'à Python 2 - en Python 3, tout ce qui précède créera des classes de nouveau style.
Voir 9. Classes (tutoriel Python), NewClassVsClassicClass et Quelle est la différence entre les classes de style ancien et nouveau en Python? pour plus de détails.
la source
Les classes de nouveau style sont celles qui sous-classent "objet" (directement ou indirectement). Ils ont un
__new__
méthode de classe en plus__init__
et ont un comportement de bas niveau un peu plus rationnel.Habituellement, vous voudrez remplacer
__getattr__
(si vous ou l'autre), sinon vous aurez du mal à prendre en charge la syntaxe "self.foo" dans vos méthodes.Informations supplémentaires: http://www.devx.com/opensource/Article/31482/0/page/4
la source
En lisant Beazley & Jones PCB, je suis tombé sur un cas d'utilisation explicite et pratique pour
__getattr__
cela aide à répondre à la partie "quand" de la question du PO. Du livre:"La
__getattr__()
méthode est un peu comme un fourre-tout pour la recherche d'attribut. C'est une méthode qui est appelée si le code essaie d'accéder à un attribut qui n'existe pas." Nous le savons par les réponses ci-dessus, mais dans la recette PCB 8.15, cette fonctionnalité est utilisée pour implémenter le modèle de conception de délégation . Si l'objet A possède un attribut objet B qui implémente de nombreuses méthodes auxquelles l'objet A souhaite déléguer, plutôt que de redéfinir toutes les méthodes de l'objet B dans l'objet A uniquement pour appeler les méthodes de l'objet B, définissez une__getattr__()
méthode comme suit:où _b est le nom de l'attribut de l'objet A qui est un objet B. Lorsqu'une méthode définie sur l'objet B est appelée sur l'objet A, la
__getattr__
méthode sera invoquée à la fin de la chaîne de recherche. Cela rendrait également le code plus propre, car vous n'avez pas de liste de méthodes définies uniquement pour la délégation à un autre objet.la source