There’s been quite some discussion on python-dev about PEP 318, “Function/Method Decorator Syntax”. PEP 318 proposes an extension that would allow function and class definitions to be modified—“decorated”—immediately after definition.
The PEP also proposes a similar extension for class syntax. Bob Ippolito commented, ”.. but who knows what else it might be useful for… I imagine it would be the end to some metaclass abuses…”.
Here are two uses of class decorators. Both could be done with metaclasses, but are clearer with PEP 318 syntax:
Defines a class with a single instance. The instance is assigned the name of the class. Pro: Design intent explicit. Con: The class object no longer has a module level name.
class Foo(object) [singleton]:
def bar(self):
print 'hi'
Foo.bar()
Sometimes you need to register all instances of a class by some attribute. This is a classic use of metaclasses, but the PEP318 syntax is simpler to read and simpler to explain:
class Foo(object) [registerByName]:
def __init__(self, name):
self.name = name
Foo(‘fred)
print Foo.Get(‘fred’)
There hasn’t been a lot of discussion (this time around) about whether the list of decorators should go before or after the name being defined. I think it works better before the name, nearer the class keyword:
class [singleton] Foo(object):
# ...
I read this as, “A singleton class named Foo which inherits from object”. I prefer this to:
class Foo(object) [singleton] :
# ...
which I read as, “A class named Foo which inherits from object and is a singleton”.
I prefer the first form because the [singleton] decorator radically modifies the meaning of the class keyword. This argument carries less weight for a decorator such as [registerByName].
This same argument applies to function decorators: having the decorator list near the def reads more clearly where the decorator changes the meaning of the def keyword, such as with a [classmethod] decorator. There is less of a case for putting the decorator list first when using decorators with less semantic effect, such as [synchronized].
Comments
Although I mostly agree with you, I prefer the post-arguments syntax for aesthetic and practical reasons. My eyes are trained to read "def binding" and "class binding", anything else will take getting used to.
Also, as you said, I'd have to emphasize that the decorator doesn't necessarily dramatically effect the meaning of the statement. In fact, I would argue that the biggest users of this syntax will NOT be using it to dramatically change the meaning of the given block. For example, many of PJE's examples have been related to using them to declare interfaces and register adapters, which would leave the class or function largely or entirely untouched. The use cases I've been demonstrated have also been largely metadata related (attaching function signatures so that things bridge properly in PyObjC). These are the sorts of things (using decorators for stashing metadata and utilizing registries) that will tend to be longer and more common, and should almost definitely be englishified as an "afterthought" when defining a function or class.
I think it's a shame most people aren't thinking beyond synchronized, classmethod and staticmethod, but most people aren't on the 'cutting edge' of Python either.
This doesn't even deal with property, which to me is the most useful and interesting decorator (of the standard set). And if I imagine other kinds of decorators, they would probably be more like property than classmethod -- they'd be composites made up of several objects.
What we need is something like class:, only without the class connotation. A way of building structures that aren't classes. You can do this with metaclasses and the class statement just fine, actually, but it feels like an abuse because it isn't really a class.
People are already using class statements this way quite a bit -- as a way of doing nested configuration or other declarative, rich data structures where dictionaries aren't syntactically attractive.
Maybe what I'm looking for is anonymous classes with a singleton instance. Or something like that. Maybe it would be okay to overload class with this kind of use, if we all agree to do so and don't freak out when we see it being done.
(BTW, there's something about this site that renders really horribly on Mozilla for me, with negative margins)
Yes, metaclasses are currently used for the purpose you describe (look at PEAK, for example).
A class decorator might make properties somewhat cleaner, because you don't need all the indirection and associated weirdness. But then again, if you use a metaclass, you don't *actually* need to create a finalized class object.. where with the decorator syntax you do finish creating the class before transforming it.
The real power of the decorator syntax is mixing and matching decorators, and being able to do it on single blocks of code. You can't really do that with metaclasses in a way that's easy enough to be worth doing.
The PEAK URL is http://peak.telecommunity.com/Articles/WhatisPEAK.html
Ian, we are trying very very hard in this discussion to not rehash everything that was said last time this came up (in Jan/Feb 2003, start here:
http://mail.python.org/pipermail/python-dev/2003-January/032337.html
). You are correct that PEP 318 doesn't help for properties. It was never meant to! It's not clear that something that would help for properties would help for classmethods et al.
> You are correct that PEP 318 doesn't help for properties.
Why? I guess it can be useful for properties too. Something like
class MyClass:
....class myprop(property) [instantiate]:
........def fget(self, inst):
............return somefunc(inst)
........def fset(self, inst, value):
............otherfunc(inst, value)
Grouped property methods at least are more tentative.
As a slightly sideways exercise, imagine how Python might look if it didn't special-case class and function assignment syntax in the first place. eg:
foo = 1
Bar = class(parents): ...
baz = fn(args): ...
One of the advantages of this approach is that it lets you lead with modifiers without obscuring the class or function name as C-style syntax tends to do, nor the parents/args list as trailing modifiers are likely to:
Foo = singleton class(object):
. . bar = static fn(*args): ...
Not a practical solution here, obviously, but sometimes taking a few extra steps backwards can help bring some fresh perspective to a problem.
Thanks for the code!