Seite 1 von 1
function decorator
Verfasst: Donnerstag 24. Dezember 2009, 21:45
von kostonstyle
hallo miteiander
habe im netz nach erklärung von function decorator gesucht, aber leider nichts verständliches gefunden. Könnte jemand mir bitte erklären was function decoratoren sind?
Vielen Dank kostonstyle
Verfasst: Donnerstag 24. Dezember 2009, 22:01
von kostonstyle
habe ein beispiel aus dem buch python
Code: Alles auswählen
class CacheDecorator(object):
def __init__(self):
self.cache = {}
self.func = None
def cachedFunc(self, *args):
if args not in self.cache:
self.cache[args] = self.func(*args)
return self.cache[args]
def __call__(self, func):
self.func = func
return self.cachedFunc
@CacheDecorator()
def fak(n):
ergebnis = 1
for i in xrange(2, n+1):
ergebnis *= i
return ergebnis
print(fak(20))
print(fak(12))
print(fak(29))
print(fak(20))
print(fak(30))
die ausgabe
Code: Alles auswählen
2432902008176640000
479001600
8841761993739701954543616000000
2432902008176640000
265252859812191058636308480000000
das programm holt, die berechneten Zahlen aus dem Speicher.
Ich habe mal versucht das Programm zu beduggen und habe folgendes festgestellt.
Der Decorator führt zuerst den Programmcode aus, der hinter @ steht. Danach geht er wieder zurück nach fak funktion und anschliessend geht er wieder zurück in die klasse.
Das war zwar keine grosse feststellung.....
Gruss kostonstyle
Verfasst: Donnerstag 24. Dezember 2009, 23:15
von DasIch
Ein Dekorator wird mit dem dekorierten Objekt aufgerufen, welches mit dem Objekt ersetzt wird dass der Dekorator zurückgibt. Üblicherweise gibt der Dekorator die gleiche Funktion/Klasse modifiziert oder gewrappt zurück.
Das von dir gezeigte Beispiel ist nicht so gut, man würde dafür üblicherweise eine Funktion nehmen.
Code: Alles auswählen
from functools import wraps
def cached(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
Verfasst: Freitag 25. Dezember 2009, 00:35
von cofi
Weil DasIch noch nicht explizit gesagt hat (und mir das geholfen hat Dekoratoren zu verstehen):
ist dasselbe wie
Verfasst: Freitag 25. Dezember 2009, 10:45
von Darii
DasIch hat geschrieben:Das von dir gezeigte Beispiel ist nicht so gut, man würde dafür üblicherweise eine Funktion nehmen.
Das gezeigt Beispiel ist vor allem nicht gut, weil man mit Keyword-Argumenten Probleme kriegt und es Namenskonventionen nicht beachtet, aber gegen Klassen ist nichts einzuwenden.
Verfasst: Freitag 25. Dezember 2009, 12:13
von birkenfeld
Darii hat geschrieben:DasIch hat geschrieben:Das von dir gezeigte Beispiel ist nicht so gut, man würde dafür üblicherweise eine Funktion nehmen.
Das gezeigt Beispiel ist vor allem nicht gut, weil man mit Keyword-Argumenten Probleme kriegt und es Namenskonventionen nicht beachtet, aber gegen Klassen ist nichts einzuwenden.
Was für Namenskonventionen? Wie auch immer, hier eine Klasse zu verwenden, ist eindeutig Overkill. Genau dafür sind Closures doch da. "Gegen Klassen ist nichts einzuwenden" -- sagst du das auch dem Java-Programmierer, der eine Klasse "Main" schreibt, nur um eine "main"-Methode aufzurufen?
Verfasst: Freitag 25. Dezember 2009, 14:07
von Darii
birkenfeld hat geschrieben:Was für Namenskonventionen?
cachedFunc, aber ist nicht so wild, ich halte mich ja bei meinem Beispiel unten auch nicht dran
Wie auch immer, hier eine Klasse zu verwenden, ist eindeutig Overkill. Genau dafür sind Closures doch da.
Findest Dekorator-Funktionen wirklich immer lesbarer als Klassen? Wenn es der Lesbarkeit/Strukturierung dient, ist gegen eine Klasse nichts einzuwenden. Zumal manche Sachen ohne ``nonlocal`` wirklich „hacky“ werden, nur mit closures.
Code: Alles auswählen
class cached(object):
def __init__(self, func):
self.cache = {}
self.func = func
def __call__(self, *args, **kwargs):
cache_key = (args, tuple(kwargs.items()))
if cache_key not in self.cache:
self.cache[cache_key] = self.func(*args, **kwargs)
return self.cache[cache_key]
@cached
def fac(n):
if not n: return 1
return n*fac(n-1)
Ich kann mir nicht helfen, ich finde es mit Klasse lesbarer. Ich habe auch nicht behauptet, dass man *immer* Klassen verwenden *muss*, aber ich wollte der unterschwelligen Verteufelung von Klassen der man hier im Forum oft begegnet, mal entgegenwirken.
"Gegen Klassen ist nichts einzuwenden" -- sagst du das auch dem Java-Programmierer, der eine Klasse "Main" schreibt, nur um eine "main"-Methode aufzurufen?
Sei doch ehrlich, der Vergleich hinkt. Das wäre nur ein Fall von: Java-Kenntnisse unreflektiert auf Python übertragen.
Verfasst: Freitag 25. Dezember 2009, 15:27
von kostonstyle
bitte das Thema nicht noch komplizierter machen, bleiben wir bei der Sache.
bei diesem Beispiel
Code: Alles auswählen
class CacheDecorator(object):
def __init__(self):
self.cache = {}
self.func = None
def cachedFunc(self, *args):
if args not in self.cache:
self.cache[args] = self.func(*args)
return self.cache[args]
def __call__(self, func):
self.func = func
return self.cachedFunc
@CacheDecorator()
def fak(n):
ergebnis = 1
for i in xrange(2, n+1):
ergebnis *= i
return ergebnis
print(fak(20))
print(fak(12))
print(fak(29))
print(fak(20))
print(fak(30))
wie ist hier der Ablauf überhaupt. Warum wird hier nur die Funktion cachedFunc aufgerufen. Wie sieht es hier ausgeschrieben aus?
a = CacheDecorator(fak(n)) ?
Noch eine Frage zu der Vererbung, dieser Code hier
Code: Alles auswählen
class DahlManager(models.Manager):
def get_query_set(self):
return super(DahlManager, self).get_query_set().filter(author='Roald Dahl')
Die Klasse DahlManager erbt von der Klasse Manager. Der Aufruf von Basisklasse super(DahlManager, ..... das hängt bei mir. Wenn ich die Definition von Manager anschaue
Code: Alles auswählen
class Manager(object):
# Tracks each time a Manager instance is created. Used to retain order.
creation_counter = 0
def __init__(self):
super(Manager, self).__init__()
self._set_creation_counter()
self.model = None
self._inherited = False
hier wird wieder der constructor von der basisklasse aufgerufen, aber warum wird hier das Manager Objekt übergeben?
Danke kostonstyle
Verfasst: Freitag 25. Dezember 2009, 15:56
von Darii
kostonstyle hat geschrieben:
wie ist hier der Ablauf überhaupt. Warum wird hier nur die Funktion cachedFunc aufgerufen. Wie sieht es hier ausgeschrieben aus?
a = CacheDecorator(fak(n)) ?
Nein, wie cofi schon schrieb,
entspricht
Was in diesem Fall bedeutet, dass fac der Rückgabewert von CacheDecorator.__call__ zugewiesen wird.
Noch eine Frage zu der Vererbung, dieser Code hier
Bitte ein eigenes Thema aufmachen.
Verfasst: Freitag 25. Dezember 2009, 16:00
von kostonstyle
meinst du das so
aber von wo weiss der Compiler, das er die Methode cachedFunc aufrufen muss. Könntest du bitte nicht den Ablauf zeigen?
Verfasst: Freitag 25. Dezember 2009, 16:04
von birkenfeld
Darii hat geschrieben:birkenfeld hat geschrieben:Was für Namenskonventionen?
cachedFunc, aber ist nicht so wild, ich halte mich ja bei meinem Beispiel unten auch nicht dran

Achso, ich dachte, du meintest DasIhm sein Snippet
Wie auch immer, hier eine Klasse zu verwenden, ist eindeutig Overkill. Genau dafür sind Closures doch da.
Findest Dekorator-Funktionen wirklich immer lesbarer als Klassen? Wenn es der Lesbarkeit/Strukturierung dient, ist gegen eine Klasse nichts einzuwenden. Zumal manche Sachen ohne ``nonlocal`` wirklich „hacky“ werden, nur mit closures.
Stimmt, deswegen gibts ja nonlocal in neueren Versionen
Code: Alles auswählen
class cached(object):
def __init__(self, func):
self.cache = {}
self.func = func
def __call__(self, *args, **kwargs):
cache_key = (args, tuple(kwargs.items()))
if cache_key not in self.cache:
self.cache[cache_key] = self.func(*args, **kwargs)
return self.cache[cache_key]
@cached
def fac(n):
if not n: return 1
return n*fac(n-1)
Ich kann mir nicht helfen, ich finde es mit Klasse lesbarer. Ich habe auch nicht behauptet, dass man *immer* Klassen verwenden *muss*, aber ich wollte der unterschwelligen Verteufelung von Klassen der man hier im Forum oft begegnet, mal entgegenwirken.
Ganz im Gegenteil; verteufeln tu ich Klassen nicht. Es ist nur bei manchen objektorientierten Sprachen so, dass man Klassen nicht nur dazu einsetzen darf, wozu sie da sind, sondern für alle anderen Fälle auch -- und da sind moderne

Sprachen einfach besser dran.
"Gegen Klassen ist nichts einzuwenden" -- sagst du das auch dem Java-Programmierer, der eine Klasse "Main" schreibt, nur um eine "main"-Methode aufzurufen?
Sei doch ehrlich, der Vergleich hinkt. Das wäre nur ein Fall von: Java-Kenntnisse unreflektiert auf Python übertragen.
Da hast du recht, das war ein bisschen polemisch. Aber im Kern stimmt es, dass wer die Möglichkeiten, die man in Python ganz ohne eine Klasse zu schreiben hat, nicht kennt, nicht ausnutzt oder für unschön hält, was verpasst.
Verfasst: Freitag 25. Dezember 2009, 16:23
von HWK
kostonstyle hat geschrieben:
meinst du das so
aber von wo weiss der Compiler, das er die Methode cachedFunc aufrufen muss. Könntest du bitte nicht den Ablauf zeigen?
Damit wird eine Instanz der Klasse erzeugt, d.h. __init__ aufgerufen.
Damit wird die Instanz, d.h. __call__ aufgerufen. __call__ liefert self.cachedFunc zurück, was an fac gebunden wird. D.h. beim Aufruf von fac wird self.cachedFunc aufgerufen.
MfG
HWK
Verfasst: Freitag 25. Dezember 2009, 16:29
von Darii
kostonstyle hat geschrieben:
meinst du das so
Nein, ich meine es so, wie ich es geschrieben habe.
aber von wo weiss der Compiler, das er die Methode cachedFunc aufrufen muss. Könntest du bitte nicht den Ablauf zeigen?
Probier es doch einfach selbst mal aus, notfalls probier die Zeile Buchstabe für Buchstabe aus.