[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