Den Unterschied zwischen __getattr__ und __getattribute__ verstehen


206

Ich versuche den Unterschied zu verstehen __getattr__und __getattribute__versage es jedoch.

Die Antwort auf die Frage zum Stapelüberlauf Der Unterschied zwischen __getattr__vs__getattribute__ lautet:

__getattribute__wird aufgerufen, bevor die tatsächlichen Attribute des Objekts angezeigt werden, und kann daher schwierig zu implementieren sein. Sie können sehr leicht in unendliche Rekursionen geraten.

Ich habe absolut keine Ahnung, was das bedeutet.

Dann heißt es weiter:

Sie wollen mit ziemlicher Sicherheit __getattr__.

Warum?

Ich habe gelesen, dass wenn __getattribute__fehlschlägt, __getattr__aufgerufen wird. Warum gibt es zwei verschiedene Methoden, die dasselbe tun? Was soll ich verwenden, wenn mein Code die neuen Stilklassen implementiert?

Ich suche nach Codebeispielen, um diese Frage zu klären. Ich habe nach besten Kräften gegoogelt, aber die Antworten, die ich gefunden habe, diskutieren das Problem nicht gründlich.

Wenn es Unterlagen gibt, bin ich bereit, diese zu lesen.


2
Wenn es hilft, aus den Dokumenten "Um eine unendliche Rekursion in dieser Methode zu vermeiden, sollte ihre Implementierung immer die Basisklassenmethode mit demselben Namen aufrufen, um auf alle Attribute zuzugreifen, die sie benötigt, z. B. Objekt .__ getattribute __ (self, name). "" [ docs.python.org/2/reference/…
kmonsoor

Antworten:


305

Einige Grundlagen zuerst.

Bei Objekten müssen Sie sich mit ihren Attributen befassen. Normalerweise tun wir das instance.attribute. Manchmal brauchen wir mehr Kontrolle (wenn wir den Namen des Attributs nicht im Voraus kennen).

Zum Beispiel instance.attributewürde werden getattr(instance, attribute_name). Mit diesem Modell können wir das Attribut erhalten, indem wir den Attributnamen als Zeichenfolge angeben.

Gebrauch von __getattr__

Sie können einer Klasse auch mitteilen, wie sie mit Attributen umgehen soll, die sie nicht explizit verwaltet, und dies über eine __getattr__Methode tun .

Python ruft diese Methode immer dann auf, wenn Sie ein Attribut anfordern, das noch nicht definiert wurde, damit Sie definieren können, was damit geschehen soll.

Ein klassischer Anwendungsfall:

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

Vorsichtsmaßnahmen und Verwendung von __getattribute__

Wenn Sie jedes Attribut abfangen müssen, unabhängig davon, ob es vorhanden ist oder nicht , verwenden Sie __getattribute__stattdessen. Der Unterschied besteht darin, dass __getattr__nur Attribute aufgerufen werden, die tatsächlich nicht vorhanden sind. Wenn Sie ein Attribut direkt festlegen, wird es durch Verweisen auf dieses Attribut abgerufen, ohne es aufzurufen __getattr__.

__getattribute__ wird immer aufgerufen.


"Zum Beispiel würde instance.attribute werden getattr(instance, attribute_name)." Sollte es nicht sein __getattribute__(instance, attribute_name)?
Md. Abu Nafee Ibna Zahid

1
@ Md.AbuNafeeIbnaZahid getattrist eine integrierte Funktion. getattr(foo, 'bar')ist äquivalent zu foo.bar.
wizzwizz4

Erwähnenswert ist, dass __getattr__nur aufgerufen wird, wenn es definiert ist, da es nicht vonobject
joelb

94

__getattribute__ wird aufgerufen, wenn ein Attributzugriff erfolgt.

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

Dies führt zu einer unendlichen Rekursion. Der Schuldige hier ist die Linie return self.__dict__[attr]. Stellen wir uns vor (es ist nah genug an der Wahrheit), dass alle Attribute in self.__dict__ihrem Namen gespeichert sind und unter diesem Namen verfügbar sind. Die Linie

f.a

versucht, auf das aAttribut von zuzugreifen f. Dies ruft auf f.__getattribute__('a'). __getattribute__versucht dann zu laden self.__dict__. __dict__ist ein Attribut von self == fund so Python-Aufrufe, f.__getattribute__('__dict__')die erneut versuchen, auf das Attribut zuzugreifen '__dict__'. Dies ist eine unendliche Rekursion.

Wenn __getattr__stattdessen verwendet worden wäre

  1. Es wäre nie gelaufen, weil fes ein aAttribut hat.
  2. Wenn es ausgeführt worden wäre (sagen wir, dass Sie danach gefragt haben f.b), wäre es nicht zum Suchen aufgerufen worden, __dict__da es bereits vorhanden ist und __getattr__nur aufgerufen wird, wenn alle anderen Methoden zum Suchen des Attributs fehlgeschlagen sind .

Die 'richtige' Art, die obige Klasse mit zu schreiben, __getattribute__ist

class Foo(object):
    # Same __init__

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

super(Foo, self).__getattribute__(attr)bindet die __getattribute__Methode der 'nächsten' Oberklasse (formal die nächste Klasse in der Method Resolution Order oder MRO der Klasse) an das aktuelle Objekt selfund ruft sie dann auf und lässt dies die Arbeit erledigen.

All diese Probleme werden vermieden, indem __getattr__Python so lange ausgeführt wird, bis ein Attribut nicht gefunden wird. An diesem Punkt übergibt Python die Kontrolle an Ihre __getattr__Methode und lässt sie sich etwas einfallen lassen.

Es ist auch erwähnenswert, dass Sie mit in eine unendliche Rekursion geraten können __getattr__.

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

Ich werde das als Übung belassen.


60

Ich denke, die anderen Antworten haben den Unterschied zwischen __getattr__und hervorragend erklärt __getattribute__, aber eine Sache, die möglicherweise nicht klar ist, ist, warum Sie sie verwenden möchten __getattribute__. Das Coole daran __getattribute__ist, dass Sie den Punkt beim Zugriff auf eine Klasse im Wesentlichen überladen können. Auf diese Weise können Sie anpassen, wie auf Attribute auf niedriger Ebene zugegriffen wird. Angenommen, ich möchte eine Klasse definieren, in der alle Methoden, die nur ein Selbstargument verwenden, als Eigenschaften behandelt werden:

# 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

Und vom interaktiven Dolmetscher:

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

Natürlich ist dies ein dummes Beispiel und Sie würden dies wahrscheinlich nie tun wollen, aber es zeigt Ihnen die Kraft, die Sie durch Überschreiben erhalten können __getattribute__.


6

Ich habe die ausgezeichnete Erklärung anderer durchgesehen. Ich fand jedoch eine einfache Antwort von diesem Blog Python Magic Methods und__getattr__ . Alle folgenden sind von dort.

Mit der __getattr__magischen Methode können wir diese nicht vorhandene Attributsuche abfangen und etwas tun, damit es nicht fehlschlägt:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.does_not_exist # 'DOES_NOT_EXIST'
d.what_about_this_one  # 'WHAT_ABOUT_THIS_ONE'

Wenn das Attribut jedoch vorhanden ist, __getattr__wird es nicht aufgerufen:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.value = "Python"
print(d.value)  # "Python"

__getattribute__ist ähnlich wie __getattr__, mit dem wichtigen Unterschied, __getattribute__der JEDE Attributsuche abfängt , spielt es keine Rolle, ob das Attribut existiert oder nicht.

class Dummy(object):

    def __getattribute__(self, attr):
        return 'YOU SEE ME?'

d = Dummy()
d.value = "Python"
print(d.value)  # "YOU SEE ME?"

In diesem Beispiel hat das dObjekt bereits einen Attributwert. Wenn wir jedoch versuchen, darauf zuzugreifen, erhalten wir nicht den ursprünglich erwarteten Wert („Python“). Wir bekommen nur alles __getattribute__zurück. Dies bedeutet, dass wir das Wertattribut praktisch verloren haben. es ist „unerreichbar“ geworden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.