Re: [Python-Dev] Class decorators
At 10:01 AM 3/28/2006 -0800, Guido van Rossum wrote:
OK, I'm convinced (mostly by the awful hackery that Phillip so proudly exposed :-).
Just as a historical note, here's where you previously rejected the same hackery as an argument for supporting class decorators: http://mail.python.org/pipermail/python-dev/2004-March/043462.html Ironically, the subsequent discussion following the above message brought me around to your point of view. :) Or more precisely, the subsequent discussion and examples convinced me that putting class decorators on top of the class was bad for readability, vs. putting them in the body just after the docstring. As you said, "the use cases are certainly very *different* than those for function/method decorators". So at this point I'd rather see a way to make my hackery go away (or become part of the standard library in some fashion) rather than simply mimic @decorators for classes.
On 3/28/06, Phillip J. Eby
At 10:01 AM 3/28/2006 -0800, Guido van Rossum wrote:
OK, I'm convinced (mostly by the awful hackery that Phillip so proudly exposed :-).
Just as a historical note, here's where you previously rejected the same hackery as an argument for supporting class decorators:
http://mail.python.org/pipermail/python-dev/2004-March/043462.html
Ironically, the subsequent discussion following the above message brought me around to your point of view. :)
Or more precisely, the subsequent discussion and examples convinced me that putting class decorators on top of the class was bad for readability, vs. putting them in the body just after the docstring. As you said, "the use cases are certainly very *different* than those for function/method decorators".
So at this point I'd rather see a way to make my hackery go away (or become part of the standard library in some fashion) rather than simply mimic @decorators for classes.
That's fine. But there's also the C#/Java POV. I'm somehow concerned that any mechanism that puts the syntax inside the class body is somehow going to have a hackish implementation, but I'd be glad to be proven wrong, so please come up with a concrete proposal! -- --Guido van Rossum (home page: http://www.python.org/~guido/)
Phillip J. Eby wrote:
http://mail.python.org/pipermail/python-dev/2004-March/043462.html
Or more precisely, the subsequent discussion and examples convinced me that putting class decorators on top of the class was bad for readability, vs. putting them in the body just after the docstring.
I just looked at that discussion, and I didn't see any argument to refute the thesis that if decorators-on-top is good enough for functions, it should be good enough for classes. In fact it seems to have been written before the on-top decorator syntax existed, so there's no discussion of the issue at all. Seems to me that, since we now already have @-decorators for functions, a *very* good reason will be needed for requiring a completely different syntax to get exactly the same effect for classes. -- Greg
At 02:55 PM 3/29/2006 +1200, Greg Ewing wrote:
Phillip J. Eby wrote:
http://mail.python.org/pipermail/python-dev/2004-March/043462.html
Or more precisely, the subsequent discussion and examples convinced me that putting class decorators on top of the class was bad for readability, vs. putting them in the body just after the docstring.
I just looked at that discussion, and I didn't see any argument to refute the thesis that if decorators-on-top is good enough for functions, it should be good enough for classes.
In fact it seems to have been written before the on-top decorator syntax existed, so there's no discussion of the issue at all.
Seems to me that, since we now already have @-decorators for functions, a *very* good reason will be needed for requiring a completely different syntax to get exactly the same effect for classes.
And here it is: because the use cases for class decorators are different. I routinely use them with things that take numerous keyword arguments, but this isn't nearly as common of a scenario for function decorators. Also, class decorators are far more likely to be just registering the class with something -- which means they don't deserve so prominent a location as to obscure the class itself. ObDisclaimer: this is my personal experience and opinion. Others may have different use cases in mind. I'm just pointing out that if @decorator support were added for classes, I wouldn't use it, because it's not actually an improvement over what I'm doing now. Function decorators, OTOH, *are* an improvement over what I did before, so I use them.
On Tuesday 28 March 2006 22:06, Phillip J. Eby wrote:
And here it is: because the use cases for class decorators are different.
This is vague.
I routinely use them with things that take numerous keyword arguments, but this isn't nearly as common of a scenario for function decorators.
The zope.formlib decorators are commonly used with many arguments; those construct the replacements and register with a class-specific registry.
Also, class decorators are far more likely to be just registering the class with something -- which means they don't deserve so prominent a location as to obscure the class itself.
I've not looked at the Java and C# examples, so I don't claim anything about those examples. For all the cases where I'm registering classes, however, it's not a local registry, but something that lives elsewhere in the system; it doesn't affect the class itself at all. I'm happy for that use case to be supported in other ways than prefixing the class with decorator-syntax stuff.
ObDisclaimer: this is my personal experience and opinion. Others may have different use cases in mind. I'm just pointing out that if @decorator support were added for classes, I wouldn't use it, because it's not actually an improvement over what I'm doing now.
So it doesn't apply. I suspect this is something for which familiarity with the use cases for the Java and C# precedents would help. For Zope 3, we have decorators that work with the component architecture (I'm sure Phillip is familiar with these). They're used with functions to indicate that the function adapts a particular kind of object, or that it implements or provides a particular interface. We have different functions that get used for this purpose in classes that are executed within the body of the class. There's some merit to being able to use a single set of functions in both cases, since the use cases are the same. I'm not sure I'd want to change the existing pattern, though, since it's already so widespread within the Zope 3 codebase (including 3rd-party components). -Fred -- Fred L. Drake, Jr. <fdrake at acm.org>
At 11:35 PM 3/28/2006 -0500, Fred L. Drake, Jr. wrote:
For Zope 3, we have decorators that work with the component architecture (I'm sure Phillip is familiar with these). They're used with functions to indicate that the function adapts a particular kind of object, or that it implements or provides a particular interface. We have different functions that get used for this purpose in classes that are executed within the body of the class. There's some merit to being able to use a single set of functions in both cases, since the use cases are the same. I'm not sure I'd want to change the existing pattern, though, since it's already so widespread within the Zope 3 codebase (including 3rd-party components).
If we're using Zope 3 as an example, I personally find that: class Foo: """Docstring here, blah blah blah """ implements(IFoo) is easier to read than: @implements(IFoo) class Foo: """Docstring here, blah blah blah """ And it only gets worse when you have lots of arguments or multiple decorators. For some reason, this doesn't bother me with functions. But then, I can't remember how often I've actually needed to use two decorators on the same function, or how many times a function decorator's arguments took multiple lines to list. Both of these are routine occurrences for my class use cases. It's too bad this syntax is ambiguous: class Foo: """Docstring here, blah blah blah """ @implements(IFoo) As this achieves a desirable highlighting of the specialness, without forcing the decorator outside the class. Oh well.
On Wednesday 29 March 2006 00:01, Phillip J. Eby wrote:
If we're using Zope 3 as an example, I personally find that:
class Foo: """Docstring here, blah blah blah """ implements(IFoo)
is easier to read than:
I think the existing usage for classes is perfectly readable. The @-syntax works well for functions as well.
For some reason, this doesn't bother me with functions. But then, I can't remember how often I've actually needed to use two decorators on the same function, or how many times a function decorator's arguments took multiple lines to list.
For zope.formlib actions, I find there's usually only one decorator. Sometimes it fits comfortably on one line, and sometimes it takes two or three. For component architecture decorators, we find we commonly use two (zope.interface.implementer and zope.component.adapter) in tandem. This can be fairly verbose with multi-object adaptation, or really long package names.
It's too bad this syntax is ambiguous:
class Foo: """Docstring here, blah blah blah """ @implements(IFoo)
As this achieves a desirable highlighting of the specialness, without forcing the decorator outside the class. Oh well.
Agreed, but... guess we can't have everything. On the other hand, something like: class Foo: """Documentation is good.""" @class implements(IFoo) is not ambiguous. Hmm. It even says what it means. :-) -Fred -- Fred L. Drake, Jr. <fdrake at acm.org>
On Wednesday 29 March 2006 00:48, Fred L. Drake, Jr. wrote:
I think the existing usage for classes is perfectly readable. The @-syntax works well for functions as well.
On re-reading what I wrote, I don't think I actually clarified the point I was trying to make originally. My point wasn't that I desparately need @-syntax for class decorators (I don't), or see it as inherantly superior in some way. It's much simpler than that: I just want to be able to use the same syntax for a group of use cases regardless of whether the target is a function or a class. This fits into the nice-to-have category for me, since the use case can be the same regardless of whether I'm decorating a class or a function. (I will note that when this use case applies to a function, it's usually a module-level function I'm decorating rather than a method.) My other example, the zope.formlib example, has only ever involved a single decorator, and is always a method. It could conceivably be applied to a nested class without much of a stretch, however. -Fred -- Fred L. Drake, Jr. <fdrake at acm.org>
On Wed, Mar 29, 2006 at 01:11:06AM -0500, Fred L. Drake, Jr. wrote:
On Wednesday 29 March 2006 00:48, Fred L. Drake, Jr. wrote:
I think the existing usage for classes is perfectly readable. The @-syntax works well for functions as well.
On re-reading what I wrote, I don't think I actually clarified the point I was trying to make originally.
My point wasn't that I desparately need @-syntax for class decorators (I don't), or see it as inherantly superior in some way. It's much simpler than that: I just want to be able to use the same syntax for a group of use cases regardless of whether the target is a function or a class.
This fits into the nice-to-have category for me, since the use case can be the same regardless of whether I'm decorating a class or a function. (I will note that when this use case applies to a function, it's usually a module-level function I'm decorating rather than a method.)
Agreed, let's not have the decorator syntax argument all over again. Once someone knows how a function decorator works they should be able to guess how a class decorator works. In my old patch[1] the grammar production for decorators was: decorated_thing: decorators (funcdef|classdef) Which makes sense, once you know how to decorate one thing you know how to decorate all things. -jackdied [1] http://python.org/sf/1007991
At 12:48 AM 3/29/2006 -0500, Fred L. Drake, Jr. wrote:
Agreed, but... guess we can't have everything. On the other hand, something like:
class Foo: """Documentation is good."""
@class implements(IFoo)
is not ambiguous. Hmm. It even says what it means. :-)
Interesting. I might have to rename some of my decorators to read well that way, though. I'll play around with the idea a bit. Of course, one of my use cases for class-level declarations is to work around the fact that decorators can't be applied to arbitrary attributes of a class, only to methods. Hm. Let me see: class SomeThing: """Some docs""" @class security.permissions( foo = security.Anybody, bar = myapp.User, ... ) class SomeCommand: """usage: some_command [-v|-q] [-f FILENAME] ...""" @class options.accept( verbose = [ options.Set('-v', '--verbose', value=True, help="Be loud"), options.Set('-q', '--quiet', value=False, help="Be quiet") ], filename = [ options.Set('-f', '--file', type=str, metavar="FILENAME") ] ) Not bad. Not bad at all.
Fred L. Drake, Jr. wrote:
class Foo: """Documentation is good."""
@class implements(IFoo)
That's an interesting idea. It could be applied to functions, too: def myfunc(myargs): """Documentation is hoopy" @def biguglydecorator(longconvolutedarglist) Someone is going to object that the evaluation time and environment of the decorator is screwy. But I don't care. :-) Greg
At 10:42 AM 3/30/2006 +1200, Greg Ewing wrote:
Fred L. Drake, Jr. wrote:
class Foo: """Documentation is good."""
@class implements(IFoo)
That's an interesting idea. It could be applied to functions, too:
def myfunc(myargs): """Documentation is hoopy" @def biguglydecorator(longconvolutedarglist)
-1; there should be only one obvious way to do it. Plus, @class is a noun and reads like it's saying something about the class; @def doesn't. It's too "verby". :)
Fred L. Drake, Jr. wrote:
It's too bad this syntax is ambiguous:
class Foo: """Docstring here, blah blah blah """ @implements(IFoo)
As this achieves a desirable highlighting of the specialness, without forcing the decorator outside the class. Oh well.
Agreed, but... guess we can't have everything. On the other hand, something like:
class Foo: """Documentation is good."""
@class implements(IFoo)
is not ambiguous. Hmm. It even says what it means. :-)
This is quite reminiscent of Ruby to me, where: class Foo: implements(IFoo) basically means: class Foo: pass Foo.implements(IFoo) For a variety of reasons that doesn't work for Python, but what you propose accomplishes the same basic thing. I'm coming in a little late on all this, but I find moving the decorator inside the class statement to be a substantial improvement, even if it is also a trivial improvement ;) Anytime I've done thought experiments about using class decorators, the results is very hard to read. That classes are inherently declarative and open, while functions are imperative and closed, makes the constructs very different. -- Ian Bicking / ianb@colorstudy.com / http://blog.ianbicking.org
On Wed, 2006-03-29 at 00:01 -0500, Phillip J. Eby wrote:
For some reason, this doesn't bother me with functions. But then, I can't remember how often I've actually needed to use two decorators on the same function, or how many times a function decorator's arguments took multiple lines to list. Both of these are routine occurrences for my class use cases.
We have a couple of instances where we use multiple decorators, and even a case where one of them takes an argument. It's not too bad. I agree with your comments about class decorator readability though. -Barry
On 3/29/06, Phillip J. Eby
For Zope 3, we have decorators that work with the component architecture (I'm sure Phillip is familiar with these). They're used with functions to indicate that the function adapts a particular kind of object, or that it implements or provides a particular interface. We have different functions that get used for this purpose in classes that are executed within the
At 11:35 PM 3/28/2006 -0500, Fred L. Drake, Jr. wrote: body
of the class. There's some merit to being able to use a single set of functions in both cases, since the use cases are the same. I'm not sure I'd want to change the existing pattern, though, since it's already so widespread within the Zope 3 codebase (including 3rd-party components).
If we're using Zope 3 as an example, I personally find that:
class Foo: """Docstring here, blah blah blah """ implements(IFoo)
is easier to read than:
@implements(IFoo) class Foo: """Docstring here, blah blah blah """
Yeah, but in the first case implements(IFoo) has to do dirty hacks involving sys.getframe(), so you win in once place but lose in the other one.
On 3/28/06, Phillip J. Eby
If we're using Zope 3 as an example, I personally find that:
class Foo: """Docstring here, blah blah blah """ implements(IFoo)
is easier to read than:
@implements(IFoo) class Foo: """Docstring here, blah blah blah """
But the former also smells more of magic. A function putting a magic variable into the scope in which it is called isn't exactly elegant use of Python. You've probably read too much Zope code to be sensitive to this argument; but to me it still grates. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
participants (8)
-
Barry Warsaw
-
Fred L. Drake, Jr.
-
Greg Ewing
-
Guido van Rossum
-
Gustavo Carneiro
-
Ian Bicking
-
Jack Diederich
-
Phillip J. Eby