__getattr__ Aufruf unterbinden

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
lunas
User
Beiträge: 87
Registriert: Samstag 2. Dezember 2006, 10:56

__getattr__ Aufruf unterbinden

Beitragvon lunas » Freitag 15. Juni 2007, 07:12

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

midan23
User
Beiträge: 116
Registriert: Sonntag 21. Mai 2006, 21:41
Wohnort: Müchen
Kontaktdaten:

Beitragvon midan23 » Freitag 15. Juni 2007, 07:57

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 :) )
Benutzeravatar
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Beitragvon mitsuhiko » Freitag 15. Juni 2007, 08:20

Mach eine Newstyle Klasse draus dann hast du das Problem gelöst. Statt class A machst du class A(object)
TUFKAB – the user formerly known as blackbird
BlackJack

Re: __getattr__ Aufruf unterbinden

Beitragvon BlackJack » Freitag 15. Juni 2007, 08:43

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.
Benutzeravatar
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Re: __getattr__ Aufruf unterbinden

Beitragvon mitsuhiko » Freitag 15. Juni 2007, 09:49

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.
TUFKAB – the user formerly known as blackbird
BlackJack

Beitragvon BlackJack » Freitag 15. Juni 2007, 12:05

Okay, trotzdem halte ich ein `__getattr__()` was grundsätzlich `None` zurückgibt für fehlerhaft. Das ist normalerweise nicht das, was man möchte.
Benutzeravatar
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Beitragvon mitsuhiko » Freitag 15. Juni 2007, 12:30

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)
TUFKAB – the user formerly known as blackbird
BlackJack

Beitragvon BlackJack » Freitag 15. Juni 2007, 12:54

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.

Wer ist online?

Mitglieder in diesem Forum: Bing [Bot], Google [Bot]