function decorator

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
kostonstyle
User
Beiträge: 148
Registriert: Sonntag 2. November 2008, 12:13

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
kostonstyle
User
Beiträge: 148
Registriert: Sonntag 2. November 2008, 12:13

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
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

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
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Weil DasIch noch nicht explizit gesagt hat (und mir das geholfen hat Dekoratoren zu verstehen):

Code: Alles auswählen

@wrap
def func():
    ...
ist dasselbe wie

Code: Alles auswählen

def func():
    ...
func = wrap(func)
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

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.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

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?
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

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.
kostonstyle
User
Beiträge: 148
Registriert: Sonntag 2. November 2008, 12:13

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
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

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,

Code: Alles auswählen

@CacheDecorator()
def fac(n): pass
entspricht

Code: Alles auswählen

def fac(n): pass
fac = CacheDecorator()(fac)
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.
kostonstyle
User
Beiträge: 148
Registriert: Sonntag 2. November 2008, 12:13

Code: Alles auswählen

def fac(n): pass
fac = CacheDecorator()(fac)
meinst du das so

Code: Alles auswählen

def fac(n): pass
fac = CacheDecorator(fac)
aber von wo weiss der Compiler, das er die Methode cachedFunc aufrufen muss. Könntest du bitte nicht den Ablauf zeigen?
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

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.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

kostonstyle hat geschrieben:

Code: Alles auswählen

def fac(n): pass
fac = CacheDecorator()(fac)
meinst du das so

Code: Alles auswählen

def fac(n): pass
fac = CacheDecorator(fac)
aber von wo weiss der Compiler, das er die Methode cachedFunc aufrufen muss. Könntest du bitte nicht den Ablauf zeigen?

Code: Alles auswählen

CacheDecorator()
Damit wird eine Instanz der Klasse erzeugt, d.h. __init__ aufgerufen.

Code: Alles auswählen

(fac)
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
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

kostonstyle hat geschrieben:

Code: Alles auswählen

def fac(n): pass
fac = CacheDecorator()(fac)
meinst du das so

Code: Alles auswählen

def fac(n): pass
fac = CacheDecorator(fac)
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.
Antworten