Seite 1 von 1

__getattr__ Aufruf unterbinden

Verfasst: Freitag 15. Juni 2007, 07:12
von lunas
Hallo,

ich habe ein recht merkwürdiges Problem, das mich schon eine Weile beschäftigt hat. Vielleicht kann mir ja hier jemand helfen.

Ich habe das Problem soweit eingegrenzt und eine kleine Testklasse zum Reproduzieren geschrieben.

In der Klasse ist die Methode getattr definiert. Außerdem wird die Klasseninstanz als Index für ein dict (testi) benutzt. Das Problem ist nun, dass der Aufruf "testi[self] = num" scheinbar ein Attribut __hash__ aufruft, was nicht definiert ist. Folglich landet der Aufruf in getattr und wird nicht bearbeitet. Es wird None zurück gegeben (was als Index für das dict wohl nicht okay ist) und das Programm verabschiedet sich mir:

Code: Alles auswählen

TypeError: 'NoneType' object is not callable
Weiß jemand Rat? Muss ich __hash__ selbst implementieren? Kann ich irgendwie vermeiden, dass nicht implementierte Attribut-Aufrufe in getattr landen oder kann ich sie wenigstens irgendwie weiterleiten?


Vielen Dank schon mal im Voraus.

lunas

Code: Alles auswählen

testi = {}

class A:
    def add (self,num):
        testi[self] = num

    def __getattr__ (self, name):
        print 'attribute:', name


a = A()
a.add (42)

print testi


Verfasst: Freitag 15. Juni 2007, 07:57
von midan23
Also ich weiss nicht so recht ...

Auf den ersten Blick sieht es so aus, als ob eine Klasse mit Daten arbeiten soll, die global verfügbar sein sollen ... was natürlich Unsinn ist.

Das Problem dürfte folgende Zeile sein:

Code: Alles auswählen

 testi[self] = num
testi ist ein dict, als Index wird aber eine Klasse angegeben ... also sucht Python in dieser Klasse nach der hash-Methode. Dazu wird __getattr__ verwendet ... was bei dieser Klasse immer None zurück liefert ...

(Es ist noch relativ früh ... daher alle Angaben ohne Gewähr :) )

Verfasst: Freitag 15. Juni 2007, 08:20
von mitsuhiko
Mach eine Newstyle Klasse draus dann hast du das Problem gelöst. Statt class A machst du class A(object)

Re: __getattr__ Aufruf unterbinden

Verfasst: Freitag 15. Juni 2007, 08:43
von BlackJack
lunas hat geschrieben:Weiß jemand Rat? Muss ich __hash__ selbst implementieren? Kann ich irgendwie vermeiden, dass nicht implementierte Attribut-Aufrufe in getattr landen oder kann ich sie wenigstens irgendwie weiterleiten?
Wenn nichtvorhandene Attribute nicht zu einem Aufruf von `__getattr__()` führen, wäre die Methode sinnlos. Diese Methode soll zu einem Attributnamen den Wert liefern oder falls das Attribut nicht existiert einen `AttributeError` auslösen. Wenn Deine Implementierung zu jedem Namen `None` liefert und das nicht gewünscht ist, solltest Du Dein `__getattr__()` reparieren.

Je nachdem was für ein Verhalten in Bezug auf Dictionaries gewünscht ist, muss man eine eigene `__hash__()`-Methode implementieren falls man `__cmp__()` oder `__eq__()` auch implementiert hat. Ist `__hash__()` nicht implementiert, wird die "Objektidentität" verwendet, d.h. alle Objekte von dem Typ sind verschieden.

Re: __getattr__ Aufruf unterbinden

Verfasst: Freitag 15. Juni 2007, 09:49
von mitsuhiko
BlackJack hat geschrieben:Wenn nichtvorhandene Attribute nicht zu einem Aufruf von `__getattr__()` führen, wäre die Methode sinnlos. Diese Methode soll zu einem Attributnamen den Wert liefern oder falls das Attribut nicht existiert einen `AttributeError` auslösen. Wenn Deine Implementierung zu jedem Namen `None` liefert und das nicht gewünscht ist, solltest Du Dein `__getattr__()` reparieren.
Das ist eher ein classobj Bug der mit type gelöst wurde. Newstyle Classes haben ein default __hash__, Klassen ohne hash (oldstyle) bekommen von hash() dann einen verpasst. Weil jetzt ein __hash__ existiert, aber halt None liefert regt sich dict auf. Von daher ist es in dieser Situation das Beste eine Newstyle Klasse zu verwenden.

Verfasst: Freitag 15. Juni 2007, 12:05
von BlackJack
Okay, trotzdem halte ich ein `__getattr__()` was grundsätzlich `None` zurückgibt für fehlerhaft. Das ist normalerweise nicht das, was man möchte.

Verfasst: Freitag 15. Juni 2007, 12:30
von mitsuhiko
BlackJack hat geschrieben:Okay, trotzdem halte ich ein `__getattr__()` was grundsätzlich `None` zurückgibt für fehlerhaft. Das ist normalerweise nicht das, was man möchte.
Das ist aber nur mit oldstyle Klassen ein Problem weil die defaults fehlen. Ich hab schon Mehrfach sowas gesehen und nie Probleme festgestellt:

Code: Alles auswählen

class AttributeDict(object):
    def __getattr__(self, attr):
        return self.get(attr, None)

Verfasst: Freitag 15. Juni 2007, 12:54
von BlackJack
Wenn man sich da mal irgendwo vertippt bekommt man `None` für ein "nicht existierendes" Attribut zurück und irgendwo anders im Programm knallt's dann und man muss den Weg zu diesem Attributzugriff erst zurückverfolgen.