8

Desidero che diverse funzioni siano eseguibili solo se l'utente che ha effettuato l'accesso ha il livello di autorizzazione richiesto.decoratore per impostare gli attributi della funzione

Per rendere la mia vita più complessa, semplicemente voglio usare decoratori. Qui di seguito cercherò di impostare l'attributo permission sulle funzioni "decorate", come mostrato di seguito.

def permission(permission_required): 
    def wrapper(func): 
     def inner(*args, **kwargs): 
      setattr(func, 'permission_required', permission_required) 
      return func(*args, **kwargs) 
     return inner 
    return wrapper 

@permission('user') 
def do_x(arg1, arg2): 

    ... 

@permission('admin') 
def do_y(arg1, arg2): 
    ... 

Ma quando lo faccio:

fn = do_x 
if logged_in_user.access_level == fn.permission_required: 
    ... 

ottengo un errore 'function' object has no attribute 'permission_required'

Che cosa mi manca?

+1

Come nota a margine: sono abbastanza sicuro che si desidera utilizzare [ 'functools.wraps'] (http://docs.python.org/2/library/functools.html#functools.wraps) Qui. Non per risolvere direttamente il tuo problema, ma perché è quasi impossibile eseguire il debug di questo tipo di codice quando tutte le funzioni finiscono con il nome 'inner', prendendo' (* args, ** kwargs) ',' inspect'ing alla fonte sbagliata, ecc. – abarnert

risposta

14

Si sta impostando l'attributo nella funzione interna (wrapper). Non hai bisogno di una funzione wrapper affatto:

def permission(permission_required): 
    def decorator(func): 
     func.permission_required = permission_required 
     return func 
    return decorator 

vostro decoratore deve restituire qualcosa che ti sostituiscono la funzione originale. La funzione originale stessa (con l'attributo aggiunto) andrà bene per quello, perché tutto ciò che si voleva fare è aggiungere un attributo ad esso.

Se hai ancora bisogno di un involucro, quindi impostare l'attributo della funzione involucro invece:

def permission(permission_required): 
    def decorator(func): 
     def wrapper(*args, **kwargs): 
      # only use a wrapper if you need extra code to be run here 
      return func(*args, **kwargs) 
     wrapper.permission_required = permission_required 
     return wrapper 
    return decorator 

Dopo tutto, si sta sostituendo la funzione avvolto con l'involucro restituito dal decoratore, quindi questo è il oggetto su cui cercherai l'attributo.

+0

Ho bisogno del wrapper per consentire argomenti nella funzione decorata. Ho usato il tuo codice e ha funzionato benissimo - ma quando ho aggiunto wrapper per gli argomenti, l'errore è tornato. – rikAtee

+1

@rikAtee: non è necessario un wrapper per consentire argomenti nella funzione decorata. Il primo esempio modifica e restituisce la funzione; prende ancora gli stessi argomenti che aveva prima di essere decorato. – abarnert

+0

@rikAtee: In effetti, il wrapper è * non * obbligatorio se tutto ciò che si fa è impostare l'attributo. Aggiungi un wrapper solo se è necessario un wrapper (ad esempio aggiungi un codice extra per operare sugli argomenti o sui valori restituiti, o fai cose extra quando viene chiamata la funzione). –

1

tuo decoratore deve restituire una funzione che può sostituire do_x o do_y, non restituisce il risultato dell'esecuzione di do_x o do_y È possibile modity si decorare come di seguito:

def permission(permission_required): 
    def wrapper(func): 
     def inner(): 
      setattr(func, 'permission_required', permission_required) 
      return func 
     return inner() 
    return wrapper 

Naturalmente, si dispone di un altro breve soluzione :

def permission(permission_required): 
    def wrapper(func): 
     setattr(func, 'permission_required', permission_required) 
     return func 
    return wrapper 
+1

La tua funzione interna non fa * niente *, quindi stai sostituendo l'originale con una funzione che non fa altro che impostare un attributo sulla funzione di avvolgimento, rendendolo * inutile *. –

Problemi correlati