Dekorator wird nur 1x ausgeführt

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
Benutzeravatar
paedubucher
User
Beiträge: 30
Registriert: Donnerstag 29. Juni 2006, 18:29
Wohnort: Schweiz
Kontaktdaten:

Hallo allerseits

Ich habe mal zwei Beispiele mit Dekoratoren geschrieben, das erste (einfachere) funktioniert perfekt:

quadrieren.py

Code: Alles auswählen

def log(func):
  def wrapper(*args):
    print '[ Log: Aufruf von', func, ']'
    return func(*args)
  return wrapper

@log
def quadrieren(x):
  return x ** 2

print quadrieren(1)
print quadrieren(2)
print quadrieren(3)
Die Ausgabe dazu lautet:

Code: Alles auswählen

[ Log: Aufruf von <function quadrieren at 0xb7dd2374> ]
1
[ Log: Aufruf von <function quadrieren at 0xb7dd2374> ]
4
[ Log: Aufruf von <function quadrieren at 0xb7dd2374> ]
9
Ich dekoriere also die Funktion "quadrieren" und logge so die Aufrufe. Dies funktioniert genau nach meiner Vorstellung.

Nun habe ich ein weiteres Beispiel mit zwei Klassen erstellt. Die erste heisst MyList und ist hier nicht weiter von Belang. MyListSub erbt von MyList und überschreibt die Methode __add__ (Operator +). Hierbei delegiere ich die Aufrufe nur nach oben, die Methode soll zudem geloggt werden. MyListSub sieht dann so aus:

mylist.py

Code: Alles auswählen

from mylist import MyList

class MyListSub(MyList):

  def __init__(self, data):
    MyList.__init__(self, data)

  def log(func):
    def wrapper(*args):
      print '[Log: Call method %s on MyListSub]' % str(func)
      print '[Log: Parameter-data: %s]' % str(args)
      return func(*args)
    return wrapper

  @log
  def __add__(self, other):
    return MyList.__add__(self, other)

if __name__ == '__main__':
  x = MyListSub('spam')
  x += ['!']

  y = MyListSub('foo')
  y += ['b']
  y += ['a']
  y += ['r']
Schön und gut, die Ausgabe sieht dann weniger erfreulich aus:

Code: Alles auswählen

[Log: Call method <function __add__ at 0x888a684> on MyListSub]
[Log: Parameter-data: (['s', 'p', 'a', 'm'], ['!'])]
[Log: Call method <function __add__ at 0x888a684> on MyListSub]
[Log: Parameter-data: (['f', 'o', 'o'], ['b'])]
>>> y
['f', 'o', 'o', 'b', 'a', 'r']
>>> 
Das __add__ an x wird einmal geloggt - es gibt ja auch nur einen Aufruf. Das __add__ an y müsste doch dreimal geloggt werden, füge ich doch auch dreimal ein Zeichen an! Wie man sieht, enthält y danach alle angefügten Zeichen, das Logging hat aber nur einmal stattgefunden (beim Ersten Aufruf mit dem Anfügen von 'b').

Was mache ich falsch?
Zuletzt geändert von paedubucher am Samstag 5. Januar 2008, 23:49, insgesamt 1-mal geändert.
BlackJack

Du könntest erst einmal Deinen gesamten Quelltext zeigen. Wie sieht `MyList` aus?

Und hast Du das im Interpreter ausprobiert? Sicher das dort alle Objekte auch wirklich die sind, von denen Du glaubst das sie es sind!?
Benutzeravatar
paedubucher
User
Beiträge: 30
Registriert: Donnerstag 29. Juni 2006, 18:29
Wohnort: Schweiz
Kontaktdaten:

BlackJack hat geschrieben:Du könntest erst einmal Deinen gesamten Quelltext zeigen. Wie sieht `MyList` aus?
Kann ich machen:
mylist.py

Code: Alles auswählen

class MyList(object):

  def __init__(self, data):
    self.data = []
    for tmp in data:
      self.data.append(tmp)

  def __add__(self, other):
    return MyList(self.data + other)

  def __mul__(self, other):
    return MyList(self.data * other)

  def __getitem__(self, offset):
    return self.data[offset]

  def __len__(self):
    return len(self.data)

  def __getslice__(self, low, high):
    return MyList(self.data[low:high])

  def append(self, obj):
    self.data.append(obj)

  def __getattr__(self, name):
    return getattr(self.data, name)

  def __repr__(self):
    return repr(self.data)
BlackJack hat geschrieben: Und hast Du das im Interpreter ausprobiert? Sicher das dort alle Objekte auch wirklich die sind, von denen Du glaubst das sie es sind!?
Ich habe es einmal mit IDLE gestartet und dann noch auf der bash via "python"-Befehl.
Zuletzt geändert von paedubucher am Samstag 5. Januar 2008, 23:49, insgesamt 1-mal geändert.
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Nach ``y += ['b']'' ist y ja auch auf einmal ein MyList-Objekt und kein MyListSub-Objekt mehr. Und MyList.__add__ hat keinen Dekorator. Ergo völlig korrektes Verhalten.
Benutzeravatar
paedubucher
User
Beiträge: 30
Registriert: Donnerstag 29. Juni 2006, 18:29
Wohnort: Schweiz
Kontaktdaten:

Trundle hat geschrieben:Nach ``y += ['b']'' ist y ja auch auf einmal ein MyList-Objekt und kein MyListSub-Objekt mehr. Und MyList.__add__ hat keinen Dekorator. Ergo völlig korrektes Verhalten.
Klingt einleuchtend. Also müsste ich in meinem unteren __add__ einfach noch die Rückgabe in eine MyListSub umwandeln. Gleich mal probieren...

Danke!

Nachtrag:

Ich habe __add__ nun überschrieben:

Code: Alles auswählen

  @log
  def __add__(self, other):
    return MyListSub(self.data + other)
Die Ausgabe lautet dementsprechend:

Code: Alles auswählen

[Log: Call method <function __add__ at 0xb7ce6614> on MyListSub]
[Log: Parameter-data: (['s', 'p', 'a', 'm'], ['!'])]
[Log: Called 1 times]
[Log: Call method <function __add__ at 0xb7ce6614> on MyListSub]
[Log: Parameter-data: (['f', 'o', 'o'], ['b'])]
[Log: Called 2 times]
[Log: Call method <function __add__ at 0xb7ce6614> on MyListSub]
[Log: Parameter-data: (['f', 'o', 'o', 'b'], ['a'])]
[Log: Called 3 times]
[Log: Call method <function __add__ at 0xb7ce6614> on MyListSub]
[Log: Parameter-data: (['f', 'o', 'o', 'b', 'a'], ['r'])]
[Log: Called 4 times]
Vielen Dank für die schnelle Hilfe :)
Zuletzt geändert von paedubucher am Samstag 5. Januar 2008, 23:49, insgesamt 1-mal geändert.
Andy
User
Beiträge: 196
Registriert: Sonntag 1. Januar 2006, 20:12
Wohnort: aus dem hohen Norden....

Code: Alles auswählen

@ padebucher: "Ich mags gern bunt! - Bitte nutze doch das Python Code-Tags. Danke!"
Benutzeravatar
paedubucher
User
Beiträge: 30
Registriert: Donnerstag 29. Juni 2006, 18:29
Wohnort: Schweiz
Kontaktdaten:

Andy hat geschrieben:

Code: Alles auswählen

@ padebucher: "Ich mags gern bunt! - Bitte nutze doch das Python Code-Tags. Danke!"
Gefällt mir auch besser, danke für den Tipp! :)
Antworten