Wie überschreibe ich __getattr__ in Python, ohne das Standardverhalten zu beeinträchtigen?

185

Ich möchte die __getattr__Methode für eine Klasse überschreiben , um etwas Besonderes zu tun, aber ich möchte das Standardverhalten nicht brechen.

Was ist der richtige Weg, um dies zu tun?

sheats
quelle
20
"Pause", fragt es, nicht "ändern". Das ist klar genug: Die "ausgefallenen" Attribute sollten die eingebauten Attribute nicht beeinträchtigen und sich so ähnlich wie möglich verhalten. Michaels Antwort ist richtig und hilfreich.
Olooney

Antworten:

267

Das Überschreiben __getattr__sollte in Ordnung sein - __getattr__wird nur als letzter Ausweg aufgerufen, dh wenn die Instanz keine Attribute enthält, die mit dem Namen übereinstimmen. Wenn Sie beispielsweise darauf zugreifen foo.bar, __getattr__wird nur aufgerufen, wenn fookein Attribut aufgerufen wurde bar. Wenn das Attribut eines ist, das Sie nicht verarbeiten möchten, erhöhen Sie AttributeError:

class Foo(object):
    def __getattr__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            raise AttributeError

Doch im Gegensatz zu __getattr__, __getattribute__zunächst wird (funktioniert nur für neue Stilklassen diejenigen also , dass vererben Objekt) bezeichnet. In diesem Fall können Sie das Standardverhalten wie folgt beibehalten:

class Foo(object):
    def __getattribute__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            return object.__getattribute__(self, name)

Weitere Informationen finden Sie in den Python-Dokumenten .

Michael Williamson
quelle
Bah, deine Bearbeitung hat dasselbe, was ich in meiner Antwort gezeigt habe, +1.
12
Cool, Python scheint es nicht zu mögen, Super's anzurufen __getattr__- irgendwelche Ideen, was zu tun ist? ( AttributeError: 'super' object has no attribute '__getattr__')
Gatoatigrado
1
Ohne Ihren Code zu sehen, ist es schwer zu sagen, aber das sieht so aus, als ob keine Ihrer Superklassen getattr definiert .
Colin vH
Dies funktioniert mit hasattr auch aus folgenden Gründen: "Dies wird implementiert, indem getattr (Objekt, Name) aufgerufen wird und geprüft wird, ob eine Ausnahme ausgelöst wird oder nicht." docs.python.org/2/library/functions.html#hasattr
ShaBANG
1
-1 Dadurch wird das Standardverhalten geändert. Jetzt haben Sie ein AttributeErrorohne den Kontext des Attributs in den Ausnahmeargumenten.
wim
33
class A(object):
    def __init__(self):
        self.a = 42

    def __getattr__(self, attr):
        if attr in ["b", "c"]:
            return 42
        raise AttributeError("%r object has no attribute %r" %
                             (self.__class__.__name__, attr))

>>> a = A()
>>> a.a
42
>>> a.b
42
>>> a.missing
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __getattr__
AttributeError: 'A' object has no attribute 'missing'
>>> hasattr(a, "b")
True
>>> hasattr(a, "missing")
False
wim
quelle
Danke dafür. Ich wollte nur sicherstellen, dass die Standardnachricht korrekt ist, ohne in der Quelle herumzuwühlen.
ShawnFumo
4
Ich denke, das self.__class__.__name__sollte verwendet werden, anstatt für den self.__class__Fall, dass die Klasse überschreibt__repr__
Michael Scott Cuthbert
3
Dies ist besser als die akzeptierte Antwort, aber es wäre schön, diesen Code nicht neu schreiben zu müssen und möglicherweise die vorgelagerten Änderungen zu verpassen, falls der Wortlaut in Zukunft geändert oder dem Ausnahmeobjekt ein zusätzlicher Kontext hinzugefügt wird.
wim
12

Um die Michael-Antwort zu erweitern __getattr__, können Sie Folgendes tun , wenn Sie das Standardverhalten beibehalten möchten :

class Foo(object):
    def __getattr__(self, name):
        if name == 'something':
            return 42

        # Default behaviour
        return self.__getattribute__(name)

Jetzt ist die Ausnahmemeldung aussagekräftiger:

>>> foo.something
42
>>> foo.error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __getattr__
AttributeError: 'Foo' object has no attribute 'error'
José Luis
quelle
2
@ fed.pavlo bist du sicher? Vielleicht hast du gemischt __getattr__und __getattribute__?
José Luis
mein Fehler. Ich habe den Anruf zu einer anderen Methode als mir selbst verpasst. ; (
fed.pavlo
1
In der Tat ist @ Michaels Antwort ohne diese Antwort unvollständig
Grijesh Chauhan