__getattribute__ wird ignoriert

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.
Antworten
Chaoswind

Folgendes problem: Ich will alle Zugriffe auf ein Object Dynamisch nach bedarf umleiten. Dafuer benutzte ich __getattribute__ da ich dachte dort werden prinzipiell alle Member eines Objects abgefragt. Nun hab ich aber festgestellt das die nicht so ist, wie das beispiel unten zeigt. __getitem__ und __add_ werden z.b. von irgendwo anders her abgefragt, aber von wo?

Code: Alles auswählen

 In [24]: class test(object):
    ....:     def __getattribute__(self, name):
    ....:         print name
    ....:         

 In [25]: test().append
append

 In [26]: test()[0]
---------------------------------------------------------------------------
exceptions.TypeError                                 Traceback (most recent call last)

/home/mai/<console> 

TypeError: unindexable object

 In [27]: test() + 2
---------------------------------------------------------------------------
exceptions.TypeError                                 Traceback (most recent call last)

/home/mai/<console> 

TypeError: unsupported operand type(s) for +: 'test' and 'int'

 In [28]: test().append()
append
---------------------------------------------------------------------------
exceptions.TypeError                                 Traceback (most recent call last)

/home/mai/<console> 

TypeError: 'NoneType' object is not callable
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi

versuche mal:

Code: Alles auswählen

class test:
    def __getattribute__(self,attr):
        print "Habe ein Attribute aufgefangen: " + attr

obj = test()
obj.value = "etwas"
print obj.value
Hilft das um __getattribue__ besser zu verstehen?
Deine eigentliche Frage verstehe zu __getitem__ und __add__ verstehe ich aber nicht? Vielleicht kann jemand anderer aushelfen oder Du schreibst nochmal etwas detaillierter was Du willst und wo das Problem liegt?

Gruß,
Christian
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

@Chaoswind: __getattribute__ fängt nur Lesezugriffe auf Attribute ab, also "objekt.attribut", nichts weiter. "objekt.attribut = 42" ist eine Zuweisung, und dafür ist __setattribute__ zuständig, "objekt[0]" ist eine Indizierung und wird durch __getitem__ behandelt, "objekt + 2" ist eine Addition und wird durch __add__ behandelt...
Chaoswind

@Joghurt
Das ist mir klar. __setattribute__, __getitem__, __add__ sind ja (bzw. sollten sein) Attribute. Und ich dachte bisher immer der zugriff auf diese Attribute laeuft über __getattribute__, wie bei anderen Funktionen auch (append z.b.).

@CM
Hast du meine Erkärung über dem Code-Beispiel gelesen? Ich will eine Klasse die abhaengig vom Zustand bestimmte Funktionen zugaenglich macht. Dafuer muss ich eben feststellen wann die jeweiligen Funktionen aufgerufen werden.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Chaoswind hat geschrieben: @CM
Hast du meine Erkärung über dem Code-Beispiel gelesen? Ich will eine Klasse die abhaengig vom Zustand bestimmte Funktionen zugaenglich macht. Dafuer muss ich eben feststellen wann die jeweiligen Funktionen aufgerufen werden.
Jau, aber scheinbar nicht verstanden. ;-) Das liegt womöglich daran, daß ich meist einen anderen Weg gehe als Du. Vielleicht hilft das weiter? Hierbei müßtest Du dann in __getattribute__ abfragen (z. B. type(name)==ListType ??), um was es sich handelt und dann dementsprechend an die verschiedensten Funktionen übergeben.
Was nun __add__ oder __getitem__ angeht würde ich dennoch explizit definieren, so wie erklärt von Joghurt. Du hast dann die Möglichkeit z. B. __add__ abhängig davon aufzurufen, was nun übergeben wird (umständlich). Oder Du kannst __add__ definieren in Abhängigkeit der Art des zweiten Operanden (direkter, aber vielleicht nicht so flexibel).

Gruß,
Christian
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Vielleicht sollte Chaoswind erstmal schreiben, was er denn genau machen will, vielleicht geht's auch ganz anders!

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Chaoswind

Im moment will ich einfach nur wissen warum __getitem__ und __add_ nicht über __getattribute__ abgefragt werden. Und wie man deren abfrage abfangen kann.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Mir scheint, Du solltest Dir vielleicht mal diesen Teil der Doku anschauen. Die Kurzfassung steht in Joghurts Erklärung. Das kann man sicher auch noch wortreicher erklären, aber es würde wirklich helfen, wenn Du nicht gar so abstrakt sagen würdest, was Du gerne machen möchtest oder wo eigentlich Dein Verständnisproblem liegt.

Vielleicht noch ein kurzes Beispiel für __add__?

Code: Alles auswählen

class Test:
    def __init__(self,name=''):
        self.name = name
    def __add__(self,other):
        return Test(self.name + other.name)

x = Test(name="Hallo ")
y = Test(name="Du")
z = x + y
print z.name
Hier mußt Du nämlich __add__ definieren, sonst bekommst Du einen wunderschönen TypeError.

Gruß,
Christian
Chaoswind

CM hat geschrieben: Hier mußt Du nämlich __add__ definieren, sonst bekommst Du einen wunderschönen TypeError.
Das weiss ich alles, ich will wissen warum das so ist. Woher weiss das Objekt das es keine __add__-Methode hat, ohne das __getattribute__ aufgerufen wird um es zu prüfen.
BlackJack

Chaoswind hat geschrieben:
CM hat geschrieben: Hier mußt Du nämlich __add__ definieren, sonst bekommst Du einen wunderschönen TypeError.
Das weiss ich alles, ich will wissen warum das so ist. Woher weiss das Objekt das es keine __add__-Methode hat, ohne das __getattribute__ aufgerufen wird um es zu prüfen.
Weil es so ist! Basta! ;-)

Das Objekt muss nicht wissen, dass es keine `__add__()` Methode hat, sondern der Interpreter -- und der schaut einfach nach.

Ich kann Dir jetzt keine explizite Stelle in der Doku nennen, aber ich würde sagen, dass die "Spezialmethoden" mit den beiden führenden und abschliessenden Unterstrichen von der `__getattribute__()` Behandlung ausgenommen sind.

Noch eine Randbemerkung: Der `TypeError` wenn Du versuchst eine Zahl zu dem Objekt ohne `__add__()` Methode zu addieren, kommt von der `__radd__()` Methode des Zahl-Objekts! Wenn der Interpreter sieht, dass das erste Objekt `__add__()` nicht definiert hat, dann wird `__radd()__` auf dem zweiten Objekt aufgerufen.

Die "genauen" Regeln dafür findet man unter coercion rules.
Chaoswind

BlackJack hat geschrieben:Das Objekt muss nicht wissen, dass es keine `__add__()` Methode hat
Und ich dachte immer Python waere Objektorientiert...
sondern der Interpreter -- und der schaut einfach nach.
Und wo schaut er nach? Vor allem, warum wird dabei der regulaere Attribute-Zugriff ignoriert? Ok, wahrscheinlich wird es __dict__ sein.
Ich kann Dir jetzt keine explizite Stelle in der Doku nennen, aber ich würde sagen, dass die "Spezialmethoden" mit den beiden führenden und abschliessenden Unterstrichen von der `__getattribute__()` Behandlung ausgenommen sind.
Nicht allgemein. Wenn ich direkt darauf zugreife laeuft das wieder über __getattribute__, also kann der Interpreter höchstens irgendwelche schmutzigen Tricks dafuer nutzen. Aber abgesehen davon macht das alles doch keinen sinn.[/quote]
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Chaoswind hat geschrieben:
BlackJack hat geschrieben:Das Objekt muss nicht wissen, dass es keine `__add__()` Methode hat
Und ich dachte immer Python waere Objektorientiert...
Ist es doch. Was hat das Eine mit dem Anderen zu tun?
Chaoswind hat geschrieben: Und wo schaut er nach? Vor allem, warum wird dabei der regulaere Attribute-Zugriff ignoriert?
In den Attributen der Klasse, resp. der Klasse. Schlägt das fehl wird __getattr__ aufgerufen, welches den Fall behandelt.
Mit __getattribute__ kann man eine Methode definieren, die __getattr__ überspringt. (Ausführliche Doku findest Du unter dem Link, den ich angab; die Doku.)
Chaoswind hat geschrieben:
BlackJack hat geschrieben:Ich kann Dir jetzt keine explizite Stelle in der Doku nennen, aber ich würde sagen, dass die "Spezialmethoden" mit den beiden führenden und abschliessenden Unterstrichen von der `__getattribute__()` Behandlung ausgenommen sind.
Nicht allgemein. Wenn ich direkt darauf zugreife laeuft das wieder über __getattribute__, also kann der Interpreter höchstens irgendwelche schmutzigen Tricks dafuer nutzen. Aber abgesehen davon macht das alles doch keinen sinn.
"Spezialmethoden" sind keineswegs bei der Behandlung durch __getattribute__() ausgenommen: Man kann es ja explizit dort definieren. Aber warum sollte man das machen? Will man z. B. zwei Instanzen addieren oder etwas zu einer Instanz addieren geht das auf direkte Weise mit __add__(). Hier nämlich schaut der Interpreter nach. __getattribute__ und __getattr__, __setattr__ sowie __delattr__ sind in meinen Augen lediglich dazu gedacht dem Programmierer ein zusätzliches Werkzeug in die Hand zu geben: Defaulthandling des Attributzugriff zu ändern. Eine schöne Sache. Wo ist das Problem? Schmutzige Tricks sind da nicht von nöten. Definiert nicht (fast) jede Sprache mit OO-features default-Zugriffe? In C++ z. B. kann man Operatoren mit dem Schlüsselwort "operator" gefolgt vom Operator überladen. In Python gibt es "special method names": Die sind um einiges deskriptiver - jedem ist klar, worum es sich bei __add__ und Konsorten handelt. Sehr praktisch. Dafür ist das Überladen eingeschränkt oder doch etwas unhandlicher, was eben daran liegt, daß es einen Interpreter gibt. Definieren muß man eine Methode immer noch - es liegt an Dir explizit zu überprüfen, ob sie existiert oder nicht und wie Du mit den Fehlermeldungen umgehst. Der Interpreter kann die Arbeit für Dich erledigen und Du schreibst nur das Exception Handling oder Du machst alles zu Fuß.

Gruß,
Christian

@BlackJack: Basta? LOL!
Chaoswind

Ist es doch. Was hat das Eine mit dem Anderen zu tun?
Weil ein Objekt sich um seine innereien zu kümmern hat, nicht irgendeine Funktion.
In den Attributen der Klasse, resp. der Klasse. Schlägt das fehl wird __getattr__ aufgerufen, welches den Fall behandelt.
Ok, dann gibt es also noch einen Aufruf vor __getattribute___. Dann bleibt die Frage warum nach diesem Aufruf nicht __getattribute__ abgefragt wird, den __getitem__ und __add__ existieren in dem beispiel nicht.
Defaulthandling des Attributzugriff zu ändern. Eine schöne Sache. Wo ist das Problem? Schmutzige Tricks sind da nicht von nöten. Definiert nicht (fast) jede Sprache mit OO-features default-Zugriffe? In C++ z. B. kann man
C++ ist nun alles andere als ein gutes beispiel fuer OO ;)
Und mir ist egal was andere Sprachen machen oder was man glaubt was OO ist oder nicht ist. Ich will wissen warum __getattribute__ in dem beispiel aufgerufen wird und wie ich das aendern kann.
BlackJack

Chaoswind hat geschrieben:
Ist es doch. Was hat das Eine mit dem Anderen zu tun?
Weil ein Objekt sich um seine innereien zu kümmern hat, nicht irgendeine Funktion.
Wer (spezielle) Methoden "dispatched", das Objekt oder der Interpreter, entscheidet nicht darüber ob die Sprache objektorientiert ist oder nicht. Sonst wären Sprachen, die so etwas schon zur Übersetzungszeit festlegen (C++, FreePascal/Delphi) oder solche die keinen Eingriff in die Nachrichtenweiterleitung erlauben (Java, C#) nicht objektorientiert. (Ich persönlich finde Python oder ObjektiveC aber auch "schöner".)
In den Attributen der Klasse, resp. der Klasse. Schlägt das fehl wird __getattr__ aufgerufen, welches den Fall behandelt.
Ok, dann gibt es also noch einen Aufruf vor __getattribute___. Dann bleibt die Frage warum nach diesem Aufruf nicht __getattribute__ abgefragt wird, den __getitem__ und __add__ existieren in dem beispiel nicht.
Die Spezialmethoden werden wahrscheinlich intern nochmal in einem Feld gehalten, damit schnellerer Code für die Operatoren erzeugt werden kann. Dann braucht man nämlich nicht über's `__dict__` zu gehen, sondern kann direkt per Offset auf die Methode zugreifen.

Es gibt bei CPython eigene Opcodes im Bytecode für die Operatoren/Spezialmethoden. Das erklärt warum bei ``a.__add__(b)`` dann doch wieder `__getattribute__()` aufgerufen wird:

Code: Alles auswählen

In [140]: def test1(a, b):
   .140.:   return a + b
   .140.:

In [141]: def test2(a, b):
   .141.:   return a.__add__(b)
   .141.:

In [142]: import dis

In [143]: dis.dis(test1)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 BINARY_ADD
              7 RETURN_VALUE

In [144]: dis.dis(test2)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_ATTR                1 (__add__)
              6 LOAD_FAST                1 (b)
              9 CALL_FUNCTION            1
             12 RETURN_VALUE
Bei dem ``LOAD_ATTR`` wird die Klasse befragt.

Die Spezialmethoden werden übrigens auch immer von der Klasse abgefragt und nicht beim Objekt. Man kann sie also auch nicht nur für ein einzelnes Objekt überschreiben, immer nur bei der Klasse.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Danke, BlackJack. Du warst um Einiges präziser.

Guten Rutsch,
Christian
Antworten