Seite 1 von 1

Dekorator wird nur 1x ausgeführt

Verfasst: Samstag 5. Januar 2008, 16:21
von paedubucher
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?

Verfasst: Samstag 5. Januar 2008, 17:50
von 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!?

Verfasst: Samstag 5. Januar 2008, 17:52
von paedubucher
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.

Verfasst: Samstag 5. Januar 2008, 18:29
von Trundle
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.

Verfasst: Samstag 5. Januar 2008, 18:31
von paedubucher
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 :)

Verfasst: Samstag 5. Januar 2008, 22:34
von Andy

Code: Alles auswählen

@ padebucher: "Ich mags gern bunt! - Bitte nutze doch das Python Code-Tags. Danke!"

Verfasst: Samstag 5. Januar 2008, 23:49
von paedubucher
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! :)