Decorator Dissection

Ron_Adam radam2 at tampabay.rr.com
Sat Apr 2 14:28:41 EST 2005


On 2 Apr 2005 08:39:35 -0800, "Kay Schluehr" <kay.schluehr at gmx.net>
wrote:

>
>There is actually nothing mysterious about decorators. 

I've heard this quite a few times now, but *is* quite mysterious if
you are not already familiar with how they work.  Or instead of
mysterious, you could say complex, as they can be used in quite
complex ways.

What is missing most with them is some really good documentation. I
got the basic idea and syntax of decorators down right away, but ran
into problems implementing them because, the structure of the
functions being used for the decorators wasn't clear.

>It is nothing
>more than ordinary function composition, executed when the decorated
>function is defined. In case of Your definition it, the composition
>rules are:
>
>decorator("Goodbye")(func)(s) = get_function(func)(s) = wrapper(s),
>where wrapper stores "Goodbye" in the local d_arg.

It worked as a model, but I mixed in concepts from cstacks and
function calls, which apparently isn't correct.  I posted another
model, it should be a bit closer. (with the Subject line spelled
correctly, continue this thread there. ;)

>Or a bit more formally we state the composition principle:
>
>Args x Func -> Func, where decorator() is a function of Args, that
>returns a function Func -> Func. As Guido had shown recently in his
>Artima blog, Func need not be an instance of an ordinary function but
>can be a function-object like his MultiMethod :
>
>http://www.artima.com/weblogs/viewpost.jsp?thread=101605

I read this, this morning it was very interesting. 

>It is also possible to extend this view by "chaining" decorators.
>
>decorator : Args(2) x (Args(1) x Func - > Func ) -> Func.
>
>To understand decorator chains it is very helpfull to accept the
>functional view instead of arguing in a procedural picture i.e. pushing
>and popping arguments onto and from the stack.

Understanding chains is next on my list. :)

>Someone asked once for a solution of the following problem that is
>similar in character to Guidos multimethod but some more general.
>
>def mul(m1,m2):
>    def default(m1,m2):
>        return "default",1+m1*m2
>    def mul_dec(m1,m2):
>        return "mul_dec",Decimal(str(m1))*Decimal(str(m2))
>    def mul_float(m1,m2):
>        return "mul_float",m1*m2
>    return (default,mul_dec,mul_float)
>
>The function mul defines the inner functions default, mul_float and
>mul_dec. What we want is a unified access to this functions by means of
>mul. Guidos solution would decompose mul in three different versions of
>mul:

This is similar to c++'s polymorphism which I've played with nearly 10
years ago. I generally found it useful only in small doses even then.
I seem to think now that c++'s version of it was implemented at
compile time, with each function call being matched up with the
correct function, by the argument types.  Where as Guido's version, is
dynamic and handles the situation at run time.  I may not be correct
in this, it's been a while.

>@multimethod(int,float)
>def mul(m1,m2):
>    return m1*m2
>
>@multimethod(float,float)
>def mul(m1,m2):
>    return m1*m2
>
>
>@multimethod(Decimal,Decimal)
>def mul(m1,m2):
>    return m1*m2
>
>but it is hard to tell, what should be done if no argument tuple
>matches.

It could then invoke the adapt() function to determine if a possible
single way to continue is available.  But with that you could run into
some very subtle bugs.  Or just annoying windows like behavior, such
as a word processor auto correcting a word when don't want it to.

>An attempt like:
>
>@multimethod(object,object)
>def mul(m1,m2):
>    return 1+m1*m2
>
>would be useless, because there is no concrete match of argument types
>onto (object,object).
>
>So I introduced an "external switch" over argument tuples, using a
>decorator chain:
>
>@case(None,"default")
>@case((float,float),'mul_float')
>@case((int,float),'mul_float')
>@case((Decimal,Decimal),'mul_dec')
>
>def mul(m1,m2):
>    def default(m1,m2):
>        return "default",1+m1*m2
>    def mul_dec(m1,m2):
>        return "mul_dec",Decimal(str(m1))*Decimal(str(m2))
>    def mul_float(m1,m2):
>        return "mul_float",m1*m2
>    return (default,mul_dec,mul_float)
>
>Can You imagine how "case" works internally?
>
>Regards,
>Kay

Sure, That should be fairly straight forward. Although I can imagine
several ways of implementing it at the moment. I think after I play
with decorator chains, one way will probably stand out as being
cleaner than the others.

Cheers,
Ron




More information about the Python-list mailing list