2010-08-14 17 views
5

Come di 2.4 (2.6 per classi), python permette di decorare una funzione con un'altra funzione:__decorated__ per decoratori Python

def d(func): return func 

@d 
def test(first): pass 

E 'una vantaggiosa zucchero sintattico. Puoi fare ogni genere di cose belle con i decoratori senza fare casino. Tuttavia, se vuoi scoprire la funzione originale che hai decorato devi saltare attraverso i cerchi (come Cls.method.__func__.__closure__[0].cell_contents o peggio).

ho trovato me stesso che desiderano per un modo migliore e ha scoperto che vi era stata qualche discussione su python-dev su come aggiungere una variabile denominata __decorated__ alla [nuova] la funzione restituito dal decoratore. Tuttavia, sembra che non sia andato da nessuna parte.

Essendo una persona avventurosa, e avendo avuto un'esperienza pitone piuttosto pesante per circa 4 anni, ho pensato di esaminare l'implementazione di __decorated__ nell'origine del compilatore Python, solo per vedere come va.

A dire il vero non ho mai approfondito la C sotto il cofano, quindi le mie prime ore hanno cercato solo di dare un senso a come funziona il codice C sottostante. Quindi, in primo luogo, quali sarebbero le migliori risorse per capire cosa dovrei cambiare/aggiungere per __decorator__?

In secondo luogo, se un decoratore restituisce una nuova funzione, __decorated__ restituisce solo la funzione originale decorata. Tuttavia, se il decoratore restituisce la funzione originale, che cosa dovrebbe accadere? Qui ci sono tre opzioni mi veniva in mente (il terzo è il mio preferito):

  1. non aggiungere __decorator__.
  2. Aggiungi __decorator__ ma impostarlo su Nessuno.
  3. Aggiungi __decorator__ e impostalo comunque sulla funzione originale.

Quindi, se dovesse succedere, quale pensi che sarebbe l'opzione migliore?

UPDATE:

Qualcun altro brought to my attention uno scenario che avevo perso. Cosa succede quando il decoratore non restituisce né la funzione originale né una funzione che avvolge l'originale? A quel punto, nulla mantiene un riferimento alla funzione originale e otterrà i dati raccolti. (Grazie Oddthinking!)

Quindi in tal caso, penso che vorrei ancora andare con la terza opzione. L'oggetto restituito dal decoratore otterrebbe un nome __decorated__ che fa riferimento alla funzione originale. Ciò significherebbe che non sarebbe raccolta di rifiuti.

Mi sembra strano che la funzione di una definizione di classe scompaia completamente perché l'hai decorata. Nella mia mente questo è ancora più motivo per avere un attributo __decorated__ applicato per ogni decoratore. Tuttavia, è più probabile che la mia intuizione sia errata e che il comportamento attuale sia quello che la maggior parte delle persone si aspetterebbe. Qualche idea?

p.s. questa è un'estensione di un precedente, più generale question che avevo. Sono andato anche per maggiori informazioni sulla prima parte con un post separate.

+0

quindi stai pensando al prossimo passaggio, cioè, come si noterà la funzione decorata che è decorata? http://stackoverflow.com/questions/3109289/how-can-python-function-access-its-own-attributes :) – mykhal

+0

Come funziona con '@ property'? Come in, i decoratori non devono restituire funzioni. – habnabit

+0

@Aaron, suppongo che aggiungerai "__decorated__" a qualsiasi cosa venga restituita, funzione o meno. –

risposta

2

Beh, indipendente da qualsiasi discussione sul fatto se questa è una buona idea, mi optare per l'opzione n. 3 perché è la più coerente: mostra sempre che la funzione è stata decorata dalla presenza dell'attributo e l'accesso al valore dell'attributo restituisce sempre una funzione, quindi non è necessario testarlo contro None.

Si dovrebbe anche considerare questo, però: cosa proponesti di fare sulla decorazione manuale? per esempio.

def test(first): pass 
test = d(test) 

Per quanto riguarda la prima parte della domanda, non ho guardato il codice sorgente dell'interprete Python molto in modo da non essere in grado di puntare a qualcosa di particolarmente utile.

+0

Buon punto sulla decorazione manuale. –

+0

Penso che dovresti aggiungere manualmente l'attributo '__decorated__' alla nuova funzione, che sarebbe ancora più brutto. –

1

Mi sento libero di utilizzare qualche singolo sottolineata "privato" attributo come _mydecor

possibile soluzione multi-decorator:

def decorate(decoration): 
    def do_decor(func): 
     if hasattr(func, '_mydecor'): 
      func._mydecor.add(decoration) 
     else: 
      func._mydecor = set([decoration]) 
     return func 
    return do_decor 

def isDecorated(func, decoration): 
    return (decoration in getattr(func, '_mydecor', set())) 

@decorate('red') 
@decorate('green') 
def orangefunc(): pass 

print isDecorated(orangefunc, 'green') # -> True 
print isDecorated(orangefunc, 'blue') # -> False 
+0

Che funziona bene se si controlla il decoratore Suppongo che potresti avvolgere il decoratore per ottenere questo risultato quando non lo controlli. –