[Python] Metodi decorati con classi

Pietro Battiston me a pietrobattiston.it
Lun 22 Apr 2013 13:53:32 CEST


Il giorno lun, 22/04/2013 alle 10.27 +0200, Marco Giusti ha scritto:
> On Mon, Apr 22, 2013 at 08:58:39AM +0200, Pietro Battiston wrote:
> [...]
> > e la poesia di un
> > 
> > class myDecoratorC(object):
> >     def __init__(self, f):
> >         print "inside myDecoratorC.__init__()"
> >         self.f = f
> > 
> >     def __call__(self, *args):
> >         print "self is", self
> >         print "args are", args
> >         print "inside myDecoratorC.__call__()"
> >         self.f( *args )
> > 
> > 
> > @myDecoratorC
> > def aFunction(*args):
> >     print "inside aFunction()"
> 
> è la prima volta che vedo una classe usata come decoratore, solitamente
> viene utilizzato un altro modo per conservare lo stato in un decoratore.

Sì, in effetti ammetto che non ho veramente bisogno di una classe (mi
faceva comodo poter usare una classe già esistente, ma posso benissimo
farlo con un wrapper, senza trasformare quella classe in callable).


> In questo caso non c'è stato da conservare ma i due seguenti casi
> dovrebbero essere equivalenti, dove il secondo è quello più usato.
> 
> 
> 	class Knights:
> 		def __init__(self, say):
> 			self.say = "Nih"
> 		def __call__(self, func):
> 			def wrapper(*args, **kwds):
> 				print "The Knights who say %s!" % self.say
> 				return func(*args, **kwds)
> 			return wrapper
> 
> 
> 	def knights_f(say):
> 		def wrapper(func):
> 			def inner(*args, **kwds):
> 				print "The Knights who say %s!" % say
> 				return func(*args, **kwds)
> 			return inner
> 		return wrapper
> 
> 	@Knights
> 	def foo():
> 		pass
> 
> 	@knights_f
> 	def bar():
> 		pass
> 
> 
> > Il problema è: come faccio ad unire gli approcci, ovvero ad avere un
> > metodo decorato da una classe? O meglio: farlo è facile, ma nel seguente
> > codice riassuntivo:
> > 
> > 
> > class myDecoratorC(object):
> >     def __init__(self, f):
> >         print "inside myDecoratorC.__init__()"
> >         self.f = f
> > 
> >     def __call__(self, *args):
> >         print "self is", self
> >         print "args are", args
> >         print "inside myDecoratorC.__call__()"
> > 
> > def myDecoratorF(func):
> >     def _decorator(self, *args):
> >         print "self is", self
> >         print "args are", args
> >         print "inside myDecoratorF()"
> >         return func(self, *args)
> >     return _decorator
> > 
> > 
> > class Decorata:
> >     @myDecoratorC
> >     def aFunction(self):
> >         print "inside aFunction()"
> >     
> >     @myDecoratorF
> >     def anotherFunction(self):
> >         print "inside anotherFunction()"
> > 
> > 
> > a = Decorata()
> > a.aFunction()
> > a.anotherFunction()
> > 
> > 
> > Il mio problema è semplicemente che i due
> >     print "self is", self
> > stampano due cose diverse.
> 
> normale perché sono due cose diverse: il primo argomento di un metodo è
> sempre l'istanza di classe, nel caso di myDecoratorC, in __call__, self
> è un'istanza di myDecoratorC. Quello che induce in errore è che non
> vengono passati argomenti a __call__, il problema è come python
> gestisce i metodi. myDecoratorC non è una "user-defined function" e
> quindi non viene creato un oggetto metodo che accetta l'istanza della
> classe Decorata. Forse è per questo che non si vede spesso oggetti usati
> come decoratori.
> 
> A questo punto mi chiedo l'utilità di avere una classe come decoratore.
> Incapsulazione? Ereditarietà?


Good point. Per quel che mi riguarda, avevo semplicemente cercato
"python decorators" su Internet, ed era uscito fuori
http://www.artima.com/weblogs/viewpost.jsp?thread=240808
che dice "the fact that decoration and calling are two clear phases when
using classes is why I argue that it's easier and more powerful this
way". Aggiungi il complesso di inferiorità alla "se mi dicono che c'è un
modo pythonesco di fare una cosa, per un paio di minuti non lo capisco
ma dal terzo minuto provo ribrezzo per qualsiasi altro modo di fare la
stessa cosa" e capisci perché mi ero intestardito.

Alla luce del tuo chiarimento però mi è chiaro che questo modo di
operare è _meno_ potente...

grazie mille

Pietro



Maggiori informazioni sulla lista Python