2012-07-30 18 views
66

Come si passa un campo di classe a un decoratore su un metodo di classe come argomento? Quello che voglio fare è qualcosa di simile:Decoratore del metodo di classe Python con argomenti autonomi?

class Client(object): 
    def __init__(self, url): 
     self.url = url 

    @check_authorization("some_attr", self.url) 
    def get(self): 
     do_work() 

Si lamenta che l'auto non esiste per il passaggio self.url al decoratore. C'è un modo per aggirare questo?

+0

È un decoratore personalizzato su cui hai il controllo o che non puoi modificare? –

+0

È il mio decoratore, quindi ho il controllo completo su di esso – Mark

+0

Viene chiamato prima di init credo sia il problema ... –

risposta

98

Sì. Invece di passare l'attributo esempio a tempo definizione della classe, controllarla compressione:

def check_authorization(f): 
    def wrapper(*args): 
     print args[0].url 
     return f(*args) 
    return wrapper 

class Client(object): 
    def __init__(self, url): 
     self.url = url 

    @check_authorization 
    def get(self): 
     print 'get' 

>>> Client('http://www.google.com').get() 
http://www.google.com 
get 

Il decoratore intercetta gli argomenti del metodo; il primo argomento è l'istanza, quindi legge l'attributo su quello. È possibile passare il nome attributo come una stringa al decoratore e utilizzare getattr se non si vuole hardcode il nome dell'attributo:

def check_authorization(attribute): 
    def _check_authorization(f): 
     def wrapper(self, *args): 
      print getattr(self, attribute) 
      return f(self, *args) 
     return wrapper 
    return _check_authorization 
2

Non è possibile. Non c'è self nel corpo della classe, perché non esiste alcuna istanza. Dovresti passare, ad esempio, uno str contenente il nome dell'attributo da cercare sull'istanza, che la funzione restituita può quindi eseguire, oppure utilizzare completamente un metodo diverso.

15
from re import search 
from functools import wraps 

def is_match(_lambda, pattern): 
    def wrapper(f): 
     @wraps(f) 
     def wrapped(self, *f_args, **f_kwargs): 
      if callable(_lambda) and search(pattern, (_lambda(self) or '')): 
       f(self, *f_args, **f_kwargs) 
     return wrapped 
    return wrapper 

class MyTest(object): 

    def __init__(self): 
     self.name = 'foo' 
     self.surname = 'bar' 

    @is_match(lambda x: x.name, 'foo') 
    @is_match(lambda x: x.surname, 'foo') 
    def my_rule(self): 
     print 'my_rule : ok' 

    @is_match(lambda x: x.name, 'foo') 
    @is_match(lambda x: x.surname, 'bar') 
    def my_rule2(self): 
     print 'my_rule2 : ok' 



test = MyTest() 
test.my_rule() 
test.my_rule2() 

ouput: my_rule2: ok

+1

Grazie mille! Questa dovrebbe essere la risposta accettata. Mi stavo strappando i capelli cercando di capire i decoratori per i metodi di classe. – benshepherd

+0

@raphael In questa configurazione non riesco ad accedere a _lambda o pattern. Come posso rimediare a questo. – Jonathan

13

A più esempio concisa potrebbe essere il seguente:

#/usr/bin/env python3 
from functools import wraps 

def wrapper(method): 
    @wraps(method) 
    def _impl(self, *method_args, **method_kwargs): 
     return method(self, *method_args, **method_kwargs) + "!" 
    return _impl 

class Foo: 
    @wrapper 
    def bar(self, word): 
     return word 

f = Foo() 
result = f.bar("kitty") 
print(result) 

che stamperà:

kitty! 
Problemi correlati