|
|
|
|
Strumenti |
18-07-2010, 10:48 | #1 |
Senior Member
Iscritto dal: Sep 2009
Città: Nel mondo dei sogni
Messaggi: 4125
|
[Guida] Python e decoratori
Ho scritto questa "guida" sui decoratori in Python dato che molti li considerano ostici e poco chiari e in italiano non c'era praticamente nulla.
Non è di certo il massimo della chiarezza e il mio talento da scrittore evidentemente non esiste o se esiste è molto nascosto Ma spero che possa essere utile a qualcuno. Buona lettura! -------------------------------------------------------------------------- I decoratori, spesso, mettono in difficoltà o fanno paura anche ai programmatori più navigati. Sarà il loro nome, sarà che a prima vista sembrano arabo, ma fattostà che è cosi. Ma cerchiamo di capire un pò meglio cosa sono questi fantomatici "decoratori". Cosa è e cosa fa un decoratore? Un decoratore è una normale funzione che modifica un'altra funzione. Quando si usa un decoratore, Python passa la funzione da decorare al decoratore, e la sostituisce con il risultato. Facciamo un esempio senza usare la sintassi tipica dei decoratori: Codice:
def mio_decoratore(funzione_da_decorare): #fai qualcosa con la funzione, per es. funzione_da_decorare.prova = "prova" return funzione_da_decorare def molt(a, b): return a * b molt = mio_decoratore(molt) Codice:
molt.prova Codice:
'prova' Codice:
def mio_decoratore(funzione_da_decorare): funzione_da_decorare.prova = "prova" return funzione_da_decorare @mio_decoratore def molt(a, b): return a * b Internamente Python applica la funzione decorata al decoratore e la sostituisce con il valore restituito da quest'ultimo. Proprio come nel primo esempio. Semplice no? Domanda: ma un decoratore deve restituire per forza una funzione? No. Può restituire tutto quello che volete, ma la sua utitlità diventa praticamente nulla Immaginate un caso come questo: Codice:
def decoratore_inutile(funzione_da_decorare): return True @decoratore_inutile def molt(a, b): return a * b Codice:
>>>molt(5, 5) >>>TypeError: 'bool' object is not callable Un decoratore può restituire una funzione arbitraria, ricordate? Questa funzione la chiameremo "wrapper" e sarà subito chiaro il perchè. Il giochetto sta nel definire la funzione wrapper all'interno del decoratore, in modo che possa utilizzare le variabili presenti nel suo name space, inclusa quindi anche la funzione da decorare che verrà passata al decoratore. Codice:
def mio_decoratore(funzione_da_decorare): def wrapper(): print "Sono dentro la funzione wrapper e posso accedere " \ "alla funzione %s" % funzione.__name__ return funzione_da_decorare() return wrapper @mio_decoratore def foo(): print "Io sono la funzione da decorare" >>>foo() Sono dentro la funzione wrapper e posso accedere alla funzione foo Io sono la funzione da decorare Semplice: li passiamo alla funzione wrapper. Questo perchè? Perchè quando usiamo il decoratore, la funzione wrapper sostituirà la funzione da decorare. Quindi sarà lei a ricevere gli argomenti in modo da poterli usare per il proprio scopo. Dal momento che un decoratore può lavorare con qualunque funzione, non sappiamo quanti argomenti avrà la funzione da decorare, quindi il problema si risolve semplicemente facendo accettare alla funzione wrapper argomenti arbitrari non posizionali e a parola chiave, che poi passerà alla funzione da decorare. Ma facciamo un esempio chiarificatore: Codice:
def mio_decoratore(funzione_da_decorare): def wrapper(*args, **kwargs): print "Stiamo chiamando la funzione %s con argomenti " \ "%s e parole chiave %s" % (funzione_da_decorare.__name__, args, kwargs) return funzione_da_decorare(*args, **kwargs) return wrapper @mio_decoratore def molt(a, b, foo = "foo"): print foo return a * b >>>molt(1, 2) Stiamo chiamando la funzione molt con argomenti (1,2) e parole chiave {} foo 2 Codice:
def mio_decoratore(funzione_da_decorare): def wrapper(*args, **kwargs): kwargs['foo'] = "argomento modificato" print "Stiamo chiamando la funzione %s con argomenti " \ "%s e parole chiave %s" % (funzione_da_decorare.__name__, args, kwargs) return funzione_da_decorare(*args, **kwargs) return wrapper @mio_decoratore def molt(a, b, foo = "foo"): print foo return a * b >>>molt(1, 2) Stiamo chiamando la funzione molt con argomenti (1,2) e parole chiave {'foo': 'argomento modificato'} argomento modificato 2 Per fare questo dobbiamo definire la nostra funzione decoratore dentro un'altra funzione, che chiameremo "opzioni". Nel momento in cui vogliamo decorare una funzione si usa la solita sintassi(@nome_decoratore), ma invece di usare il nostro decoratore useremo la funzione "opzioni". Vediamo subito un esempio: Codice:
def opzioni(valore): def mio_decoratore(funzione_da_decorare): funzione_da_decorare.prova = valore return funzione_da_decorare return mio_decoratore @opzioni('test') def molt(a, b): return a * b >>>molt(5, 5) 25 >>>molt.prova 'test' E se insieme alle opzioni vogliamo usare anche una funzione wrapper? Il procedimento è esattamente lo stesso: Codice:
def opzioni(nome): def mio_decoratore(funzione_da_decorare): def wrapper(*args, **kwargs): kwargs.update({'nome': nome}) print "Chiamo la funzione %s" % funzione_da_decorare return funzione_da_decorare(*args, **kwargs) return wrapper return mio_decoratore @opzioni("pinco") def stampa_nome(nome = None): print "Ciao ", nome Codice:
>>>stampa_nome >>><function wrapper at 0x00BBA930> Questo è più o meno tutto. Esempi d'uso, i decoratori @staticmethod, @classmethod e @property Riferimenti: modulo "decorator" scritto da Michele Simionato -------------------------------------------------------------------------- Il contenuto di questo post è rilasciato con licenza Creative Commons Attribution-Noncommercial-Share Alike 2.5 Ultima modifica di Ryuzaki_Eru : 20-07-2010 alle 11:01. |
18-07-2010, 12:29 | #2 |
Senior Member
Iscritto dal: Jan 2001
Città: Livorno
Messaggi: 1275
|
Grazie e complimenti per la tua guida, è la prima volta che sento parlare di decoratori (avrò usato python 3 volte), solo che non mi immagino un caso pratico in cui serva usarli e semplifichino la vita.
Potresti fare un esempio? Ciao |
18-07-2010, 13:04 | #3 |
Senior Member
Iscritto dal: Sep 2009
Città: Nel mondo dei sogni
Messaggi: 4125
|
Caso pratico comune: @property, @staticmethod e @classmethod. Adesso sono incasinato, tu documentati. Quando posso casomai scrivo qualche esempio.
In generale, quando hai bisogno di modificare o fare operazioni su una funzione, allora i decoratori fanno al caso tuo. Ovviamente non se ne deve abusare e quello che fai con i decoratori lo puoi fare anche senza, però semplificano la vita in alcuni casi. Esempio: mettiamo che hai scritto un'applicazione in Django e vuoi fare in modo che possa essere usata solo dall'admin. Puoi benissimo scriverti un decoratore che controlla se la vista richiesta sia stata "chiamata" dall'admin o no e quindi determinare le operazioni da seguire o sollevare un'eccezione. Ultima modifica di Ryuzaki_Eru : 18-07-2010 alle 13:12. |
19-07-2010, 07:38 | #4 |
Senior Member
Iscritto dal: Jan 2002
Città: Germania
Messaggi: 26107
|
Un caso pratico, reale, e comunissimo è il log.
Mettiamo che debba loggare la chiamata a una funzione, i suoi parametri, e il valore di ritorno. Scrivo un decoratore log che restituisce un wrapper che "ingloba" la funzione da chiamare. A questo punto quando il wrapper viene invocato, logga il nome della funzione che ha inglobato, e i parametri che gli sono stati passati. A questo punto richiama la funzione e conserva il risultato. Logga il risultato. E infine restituisce il valore al chiamante. Per qualunque funzione (o metodo) da loggare basta scrivere: Codice:
@log def MiaFunzione(): pass Ancora più interessanti sono i decoratori di classi. Si può scrivere un decoratore di classe (che è una funzione), che invece di ricevere in input una funzione, riceve una classe. A questo punto possiamo prendere l'elenco di tutti i suoi metodi e applicarci una decoratore di log, in modo che tutti i suoi metodi siano loggati. Quindi per loggare tutti i metodi di una classe basta scrivere: Codice:
@log class MiaClasse(): def f(self): pass def g(self): pass Comunque complimenti per la guida: chiara e semplice. Se aggiungi i decoratori di classe (disponibili da Python 2.6) secondo me potrebbe essere messa nella sezione dei tutorial.
__________________
Per iniziare a programmare c'è solo Python con questo o quest'altro (più avanzato) libro @LinkedIn Non parlo in alcun modo a nome dell'azienda per la quale lavoro Ho poco tempo per frequentare il forum; eventualmente, contattatemi in PVT o nel mio sito. Fanboys |
19-07-2010, 09:28 | #5 |
Senior Member
Iscritto dal: Dec 2005
Messaggi: 558
|
Ottima guida, grazie mille!
|
19-07-2010, 14:26 | #6 | |
Senior Member
Iscritto dal: Sep 2009
Città: Nel mondo dei sogni
Messaggi: 4125
|
Quote:
Comunque è un argomento interessante questo dei decoratori di classe. Appena posso l'aggiungo Se vuoi puoi anche aggiungere tu degli esempi alla tua spiegazione che hai appena scritto e il 90% è già praticamente fatto |
|
19-07-2010, 16:27 | #7 | |
Senior Member
Iscritto dal: Jun 2002
Città: Dublin
Messaggi: 5964
|
Quote:
ciao, e complimenti per il lavoro
__________________
C'ho certi cazzi Mafa' che manco tu che sei pratica li hai visti mai! |
|
19-07-2010, 17:53 | #8 |
Senior Member
Iscritto dal: Sep 2009
Città: Nel mondo dei sogni
Messaggi: 4125
|
|
19-07-2010, 21:35 | #9 | |
Senior Member
Iscritto dal: Jan 2002
Città: Germania
Messaggi: 26107
|
Quote:
Codice:
from functools import wraps def log(Function): 'Decorator to log a function call, its parameters, and its result' @wraps(Function) def Wrapper(*Args, **Keywords): print Function.__name__, Args, Keywords Result = Function(*Args, **Keywords) print Function.__name__, '->', Result return Result return Wrapper @log def f(x, y, z): return x * y * z f(1, 2, 3) @log def g(*Args, **Keywords): return 'Argomenti: %s. Keyword: %s.' % (Args, Keywords) g(1, 2, 3, y = 'a', z = 'b', x = 'c') Codice:
f (1, 2, 3) {} f -> 6 g (1, 2, 3) {'y': 'a', 'x': 'c', 'z': 'b'} g -> Argomenti: (1, 2, 3). Keyword: {'y': 'a', 'x': 'c', 'z': 'b'}. Ne uso altri anche per validare l'input, oppure per intercettare eventuali eccezioni. Coi decoratori c'è ampio spazio per l'immaginazione.
__________________
Per iniziare a programmare c'è solo Python con questo o quest'altro (più avanzato) libro @LinkedIn Non parlo in alcun modo a nome dell'azienda per la quale lavoro Ho poco tempo per frequentare il forum; eventualmente, contattatemi in PVT o nel mio sito. Fanboys |
|
20-07-2010, 11:00 | #10 |
Senior Member
Iscritto dal: Sep 2009
Città: Nel mondo dei sogni
Messaggi: 4125
|
Bene, poi lo aggiungo alla guida con i riferimenti a te. Appena posso scrivo degli esempi con @property, @staticmethod e @classmethod, e un esempio di decoratore di classe.
|
21-07-2010, 09:18 | #11 |
Senior Member
Iscritto dal: Jan 2002
Città: Germania
Messaggi: 26107
|
Lascia perdere i riferimenti a me.
__________________
Per iniziare a programmare c'è solo Python con questo o quest'altro (più avanzato) libro @LinkedIn Non parlo in alcun modo a nome dell'azienda per la quale lavoro Ho poco tempo per frequentare il forum; eventualmente, contattatemi in PVT o nel mio sito. Fanboys |
21-07-2010, 09:19 | #12 |
Senior Member
Iscritto dal: Sep 2009
Città: Nel mondo dei sogni
Messaggi: 4125
|
L'hai scritto tu quel pezzo, è giusto che metto i riferimenti
|
21-07-2010, 20:49 | #13 |
Senior Member
Iscritto dal: Apr 2005
Città: SARDEGNA
Messaggi: 1861
|
ottima spiegazione complimenti!
la prima volta che mi sono imbatutto su un decorator è stato su django... @login_required fortuna che ho intuito al volo la loro funzione!
__________________
Trattative OK non le conto più...Trattative non OK: Slashdot giuscone Kururu Paki deCri |
01-08-2010, 09:38 | #14 |
Senior Member
Iscritto dal: Sep 2009
Città: Nel mondo dei sogni
Messaggi: 4125
|
Al più presto aggiorno la guida, scriverò degli esempi sui decoratori predefiniti in Python e vedrò di fare smanettare sui decoratori di classe che sono particolari, anche se il funzionamento e il concetto è identico a quelli delle funzioni.
|
19-05-2011, 08:14 | #15 |
Senior Member
Iscritto dal: Sep 2009
Città: Nel mondo dei sogni
Messaggi: 4125
|
Noto che la guida non è stata ancora spostata nell'apposita sezione...come mai?
|
19-05-2011, 12:58 | #16 |
Senior Member
Iscritto dal: Jan 2002
Città: Germania
Messaggi: 26107
|
Forse cionci aspetta che completi l'argomento coi decoratori di classe.
__________________
Per iniziare a programmare c'è solo Python con questo o quest'altro (più avanzato) libro @LinkedIn Non parlo in alcun modo a nome dell'azienda per la quale lavoro Ho poco tempo per frequentare il forum; eventualmente, contattatemi in PVT o nel mio sito. Fanboys |
19-05-2011, 13:05 | #17 |
Senior Member
Iscritto dal: Sep 2009
Città: Nel mondo dei sogni
Messaggi: 4125
|
|
19-05-2011, 13:18 | #18 |
Senior Member
Iscritto dal: Jan 2002
Città: Germania
Messaggi: 26107
|
Allora aspetteremo. Comunque per un'introduzione ai decoratori quanto già scritto va bene.
__________________
Per iniziare a programmare c'è solo Python con questo o quest'altro (più avanzato) libro @LinkedIn Non parlo in alcun modo a nome dell'azienda per la quale lavoro Ho poco tempo per frequentare il forum; eventualmente, contattatemi in PVT o nel mio sito. Fanboys |
20-05-2011, 08:20 | #19 |
Senior Member
Iscritto dal: Sep 2009
Città: Nel mondo dei sogni
Messaggi: 4125
|
|
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 03:24.