English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
I decoratori accettano una funzione, aggiungono alcune funzionalità e la restituiscono. In questo articolo, imparerai come creare decoratori e perché è utile usarli.
Python ha una funzione interessante chiamataDecoratoriper aggiungere funzionalità al codice esistente.
questo viene anche chiamatoMeta-programmazione,Perché una parte del programma tenta di modificare un'altra parte del programma durante la compilazione.
Per comprendere i decoratori, dobbiamo prima comprendere alcune conoscenze di base di Python.
Dobbiamo accettare il fatto che tutto in Python èOggetto. I nomi che definiamo sono solo identificatori associati a questi oggetti.FunzioneAnche loro non sono esenti, sono oggetti (con attributi). Puoi associare nomi diversi allo stesso oggetto funzionale.
Questo è un esempio.
def first(msg): print(msg) first("Hello") second = first second("Hello")
Quando esegui il codice, le due funzioni first e second producono lo stesso output. In questo caso, i nomi first e second si riferiscono allo stesso oggetto funzionale.
Ora, la situazione sembra un po' più complessa, puoi passare una funzione come parametro a un'altra funzione.
Se hai già utilizzato funzioni come map, filter e reduce in Python, allora sai già questo.
Questo tipo di funzione che accetta altre funzioni come parametri è anche chiamatoAlta funzione. Questo è un esempio di tale funzione.
def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result
Chiamiamo la funzione nel modo seguente.
>>> operate(inc,3) 4 >>> operate(dec,3) 2
Inoltre, una funzione può restituire un'altra funzione.
def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() #Output "Hello" new()
In questo caso, is_returned() è una funzione annidata, che viene definita e restituita ogni volta che chiamiamo is_drawn().
Infine, dobbiamo capireClosure in Python.
Le funzioni e i metodi sono chiamatiInvocabile,Poiché possono essere chiamati.
In realtà, qualsiasi oggetto che implementi il metodo speciale __call__() è chiamato invocabile. Pertanto, nel senso più basilare, i decoratori sono invocabili e possono restituire oggetti invocabili.
In sostanza, i decoratori accettano una funzione, aggiungono alcune funzionalità e la restituiscono.
def make_pretty(func): def inner(): print("Sono stato decorato") func() return inner def ordinary(): print("Sono una funzione comune")
Quando si esegue il seguente codice in shell,
>>> ordinary() Sono una funzione comune >>> # Decoriamo questa funzione comune >>> pretty = make_pretty(ordinary) >>> pretty() Sono stata decorata Sono una funzione comune
Nel esempio mostrato, make_pretty() è un decoratore. Nel passo di assegnazione.
pretty = make_pretty(ordinary)
La funzione ordinary() è stata decorata, e la funzione restituita è chiamata pretty.
Possiamo vedere che il decoratore aggiunge alcune nuove funzionalità alla funzione originale. È come avvolgere un regalo. Il decoratore agisce come involucro. La natura dell'oggetto avvolto (il regalo dentro) non cambia. Ma ora sembra più bello (dopo l'avvolgimento).
Di solito, decoriamo una funzione e la riassegniamo come
ordinary = make_pretty(ordinary).
Questa è una costruzione comune, quindi Python ha una sintassi semplificata per questo.
Possiamo usare il simbolo @ insieme al nome del decoratore e metterlo sopra la definizione della funzione da decorare. Ad esempio:
@make_pretty def ordinary(): print("Sono una funzione comune")
è equivalente a
def ordinary(): print("Sono una funzione comune") ordinary = make_pretty(ordinary)
Questo è solo uno zucchero sintattico per implementare il decoratore.
Il decoratore sopra è semplice e si applica solo a funzioni senza parametri. Cosa succede se la nostra funzione ha come parametri quelli mostrati di seguito?
def divide(a, b): return a/b
Questa funzione ha due parametri:aeb. Sappiamo che se mettiamobSe viene passato 0, si genererà un errore.
>>> divide(2,5) 0.4 >>> divide(2,0) Traceback (chiamata più recente per ultima): ... ZeroDivisionError: divisione per zero
Ora facciamo un decoratore per verificare se questa situazione causerebbe un errore.
def smart_divide(func): def inner(a,b): print("Devo fare la divisione", a, "e", b) if b == 0: print("Ohimè! Non posso dividere") return return func(a,b) return inner @smart_divide def divide(a,b): return a/b
Se si verifica un errore, questa nuova implementazione restituirà None.
>>> divide(2,5) Devo fare la divisione 2 e 5 0.4 >>> divide(2,0) Devo fare la divisione 2 e 0 Oh no! Non posso dividere
In questo modo, possiamo decorare funzioni con parametri.
Un osservatore attento noterà che i parametri della funzione annidata all'interno del decoratore inner() sono gli stessi della funzione decorata. Considerando questo, possiamo ora rendere i decoratori universali capaci di utilizzare un numero arbitrario di parametri.
In Python, questo magia è completato tramite il function(*args, **kwargs). Così, args sono i parametri posizionali.TupleInvece di kwargs, sono i parametri chiave.Dizionario. Un esempio di decoratore di questo tipo è.
def works_for_all(func): def inner(*args, **kwargs): print("Posso decorare qualsiasi funzione") return func(*args, **kwargs) return inner
È possibile collegare più decoratori in Python.
Questo significa che una funzione può essere decorata più volte (o la stessa) con decoratori diversi. Dobbiamo solo posizionare i decoratori sopra la funzione desiderata.
def star(func): def inner(*args, **kwargs): print("*" * 30) func(*args, **kwargs) print("*" * 30) return inner def percent(func): def inner(*args, **kwargs): print("%" * 30) func(*args, **kwargs) print("%" * 30) return inner @star @percent def printer(msg): print(msg) printer("Ciao")
Questo produrrà l'output.
****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Ciao %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************
la sintassi sopra,
@star @percent def printer(msg): print(msg)
è equivalente a
def printer(msg): print(msg) printer = star(percent(printer))
L'ordine dei decoratori è importante. Se li mettiamo in ordine opposto,
@percent @star def printer(msg): print(msg)
L'esecuzione avverrà
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** Ciao ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%