[Python-Dev] method decorators (PEP 318)

Phillip J. Eby pje at telecommunity.com
Fri Mar 26 12:44:20 EST 2004


At 08:44 AM 3/26/04 -0800, Guido van Rossum wrote:

>   def foobar(self, arg):
>       @author = AuthorInfo(author="GvR", version="1.0", copyright="GPL", ...)
>       @deprecated = True

My only objection to that format, is that it looks like part of the 
function body.  It might work if moved before the colon, though, and then 
it would need some sort of bracketing.


>I find the mark-up in your example about the worst possible mark-up;
>in practice, these things can get quite voluminous (we're only
>restricting ourselves to a single item that fits on a line because
>we're used to giving minimal examples).  I would hate it if something
>*semantically* significant like classmethod or synchronized had to
>compete for the user's attention with several lines of structured
>metadata.

I can see that.  I can also see from Brad's comments that some people have 
a completely different idea of what "metadata" is than I do.  I think of 
metadata as something that will be used to drive program behavior, whereas 
what I think you and Brad are calling metadata may be more like what I 
think of as "annotation".  And I don't see an issue with having a separate 
syntax for annotation, I just don't think it's a replacement for 
program-controlling metadata.


>A framework-supplied metaclass could easily be designed to look for
>function attributes set with this mechanism.

For that to be really practical, there's going to have to be at minimum a 
user-transparent way for metaclasses to be combined.  It's hard today even 
for wizards.


>(One could also imagine
>a metaclass-free hybrid approach where there's a transformer placed in
>the decorator list which looks for function attributes to guide its
>transformation.)
>
>And yes, this could also be used to declare class and static methods
>(by putting something in the standard metaclass that looks for certain
>function attributes, e.g. @wrap=classmethod) but I still think that
>these "wrapper descriptors" are better served by a different syntax,
>one that is more integrated with the function signature.

Maybe so, although I'm still of the view (as are others) that it shouldn't 
break the symmetry of definition and invocation (i.e., 'def foo(...)' and 
'foo(...)').



> > Obviously, I'm not arguing that Python should look like Lisp.  The
> > current decorator syntax patch is *much* easier to read than
> > wrapping an entire function definition in parentheses.  But the
> > semantics that I think most people are asking for with decorators,
> > is the simple Lisp-like capability of applying transformations to a
> > function, but with a more Pythonic syntax.  That is, one where flat
> > is better than nested, and readability counts.  That is the use case
> > that decorators are intended to serve, IMO, and I believe that this
> > is what most other proponents of decorators are after as well.
>
>I would like to see more examples of that use case that aren't
>classmethod and aren't mutually exclusing with most other examples.
>The PEP stops with the arg checker example, and that's not a very
>convincing one (because it's so clumsy for that particular goal).

Okay.  A few multi-decorator idioms used in PEAK today, but rewritten using 
the patch syntax:

    def childCountMonitor(self) [events.taskFactory, binding.Make]:

The 'events.taskFactory' means that this generator will be treated as an 
event-driven psuedo-thread, and will return an 'events.Task' object 
wrapping the generator-iterator.  The 'binding.Make' means this attribute 
is a property whose value will be computed once, upon first reference to 
the attribute.  Thus, using 'self.childCountMonitor' will return
the "daemonic" pseudothread for this instance that monitors its child 
count, and it will begin running if it has not already done so.

    def syntax(self) [binding.Make, binding.classAttr]:

The 'binding.Make' means that this attribute is a once-property as 
described above, but the 'binding.classAttr' means that it will have a 
value *per class*, rather than per-instance.  In other words, the 
descriptor will be moved to the metaclass.  (This does require support from 
the metaclass, to automatically create a new metaclass for the class where 
this decorator is used).

I haven't done an exhaustive search, but I believe these are the most 
common stacking decorators I use today.  However, I would also note that 
PEAK inclines towards monolithic do-everything decorators with lots of 
keyword arguments, due to the current inconvenience of stacking them.  For 
example, this is more likely the actual spelling today:

    def childCountMonitor(self):
        ...

    childCountMonitor = binding.Make(
        events.taskFactory(childCountMonitor), uponAssembly=True
    )

which would then read either:

    def childCountMonitor(self) [
        events.taskFactory, binding.Make(uponAssembly=True)
    ]:

or maybe even better, something like:

    def childCountMonitor(self) [
        events.taskFactory, binding.Make, binding.autoAssembled
    ]:

The uponAssembly flag indicates that the attribute should be constructed as 
soon as the component has become part of a complete application 
assembly.  It might be nice to make this orthogonal to other decoration 
behaviors.

Anyway, taskFactory, Make, and classAttr are just a few of the decorators I 
use, and they get used independently quite a bit.  They're just the ones 
that are most frequently stacked.


> > Considering that both function attributes and decorators have the
> > same overhead today for use (i.e., put them at the end, no special
> > syntax available), that would suggest that the need/desire for
> > decorators is much greater than the need for mere metadata
> > annotation.
>
>I'm not convinced.  I expect that if we had good syntax for both, we
>would see more use of attributes than of decorators, except perhaps in
>meta-heavy frameworks like PEAK.

Fair enough.  I'll certainly agree that using decorators today to supply 
attributes is easier than applying function attributes directly, in cases 
where either could be used to accomplish a need.  And if there had been a 
good syntax for function attributes available, there would likely be 
several places where I'd have leveraged it in place of decorators, mostly 
to avoid the many duplicated keyword arguments among my current decorators.

I guess my main concern is that I don't want to see PEP 318 crippled by a 
syntax that only works (and then just barely!) for classmethod or another 
single-word decorator.




More information about the Python-Dev mailing list