At 11:07 AM 3/29/2006 -0800, Guido van Rossum wrote:
On 3/28/06, 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:
@implements(IFoo) class Foo: """Docstring here, blah blah blah """
But the former also smells more of magic.
My comment above was only about readable *placement* of the decorators, not the actual syntax. Many approaches to the actual syntax in the body are possible. For example, what did you think of Fred Drakes's "@class" proposal? To specify it formally, one could say that this: @class EXPR in a class scope would expand to the equivalent of: locals().setdefault('__decorators__',[]).append(EXPR) and is a syntax error if placed anywhere else. That, combined with support for processing __decorators__ at class creation time, would fulfill the desired semantics without any implicit "magic". (The locals() part could of course be implemented in bytecode as LOAD_LOCALS, since class scopes implement their locals as a dictionary. That would avoid the need for adding any new bytecodes, since this isn't a performance-sensitive feature.)
On Wed, Mar 29, 2006 at 07:23:03PM -0500, Phillip J. Eby wrote:
At 11:07 AM 3/29/2006 -0800, Guido van Rossum wrote:
On 3/28/06, 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:
@implements(IFoo) class Foo: """Docstring here, blah blah blah """
But the former also smells more of magic.
My comment above was only about readable *placement* of the decorators, not the actual syntax. Many approaches to the actual syntax in the body are possible.
For example, what did you think of Fred Drakes's "@class" proposal? To specify it formally, one could say that this:
@class EXPR
in a class scope would expand to the equivalent of:
locals().setdefault('__decorators__',[]).append(EXPR)
and is a syntax error if placed anywhere else. That, combined with support for processing __decorators__ at class creation time, would fulfill the desired semantics without any implicit "magic".
A function decorator takes a function as an argument and returns something (probably a function and maybe even the very same function). This is exactly what class decorators should do or we should call them something else and give them a distinct syntax. A function decorator is there to replace code like: def myfunc(a, b, c): # half a screen of code myfunc = mangle(myfunc) Likewise class decorators would save me from typing class MyClass: # many functions taking half a screen of code each register(MyClass, db_id=20) I used to do this with metaclasses but stopped because it requires making 'db_id' a member of the class which is magically noticed by a metaclass two files away. Using metaclasses also required gross hacks like checking for a 'DO_NOT_REGISTER' member for subclasses that wanted to inherit from a class that had a Register metaclass but didn't want to be registered. Yuck. If you want to do lots of Zopeish stuff mostly inside the class write a decorator that looks for it in the class body. @zope.find_magic_in_attr('mymagic') class MyClass: mymagic = [] # some long hairy thing Talking about something other than a decorator or proposing all new syntax is just going to get this pronounced out of existence. If-I-wanted-zope-I'd-use-zope-ly, -jackdied
At 08:00 PM 3/29/2006 -0500, Jack Diederich wrote:
A function decorator takes a function as an argument and returns something (probably a function and maybe even the very same function).
So would class decorators.
This is exactly what class decorators should do or we should call them something else and give them a distinct syntax.
Yep.
A function decorator is there to replace code like:
def myfunc(a, b, c): # half a screen of code myfunc = mangle(myfunc)
Likewise class decorators would save me from typing
class MyClass: # many functions taking half a screen of code each register(MyClass, db_id=20)
Yep.
Talking about something other than a decorator
You lost me there. Nobody's argued (AFAIK) for class decorators being anything other than single-argument functions that take a class as input and return a class as output.
or proposing all new syntax is just going to get this pronounced out of existence.
Which wouldn't be a bad thing, IMO. There's no point in adding them if they're not an actual improvement over what we can do now. Comments about the hackishness of the implementation used by Zope and PEAK are also off-base; nobody proposed that everyone should go off and implement their own such hacks! Meanwhile, inclusion of such a facility in the stdlib isn't without precedent; there are IIRC at least *6* sys._getframe hacks in the stdlib already. Nothing stops us from adding a 'decorate_class' function to the stdlib that's used like this, for example: class MyClass: decorate_class( register(db_id=20), implements(IFoo), ... ) or to just make the class machinery interpret a __decorators__ attribute (a variant on an old suggestion of Guido's): class MyClass: __decorators__ = [register(db_id=20), implements(IFoo)] The only *implementation* reason to give this special syntax, IIUC, is to allow implementations like Jython and IronPython and Pyrex to statically recognize certain decorators at compile-time. There are of course also non-implementation reasons to have class decorator syntax (such as EIBTI), and I agree with that. But readability also counts, and the readability of @decorators on the outside of a class tends to suck as the number of decorators and arguments increases. What's more, I haven't seen anybody posting any counterexamples to show that it doesn't suck for common use cases. Indeed, at the moment I don't even recall seeing any examples of class decorators being used without arguments! I also suspect that any actual Jython/IronPython examples are likely to be at least as verbose as Zope and PEAK's, and probably more likely to include multiple decorators. (Depending on how Java annotations and .Net attribs would be translated to decorators.) So, I'm personally not in favor of adding class decorators with a syntax that blindly imitates that of function decorators, without a proper examination of the use cases. This is precisely the argument that Guido used to veto class decorators in 2.4, and nothing about the issue has changed since then, except for the subject being reopened to consideration.
Phillip J. Eby wrote:
the readability of @decorators on the outside of a class tends to suck as the number of decorators and arguments increases.
So do decorators outside a function.
What's more, I haven't seen anybody posting any counterexamples to show that it doesn't suck for common use cases. Indeed, at the moment I don't even recall seeing any examples of class decorators being used without arguments!
Well, here's how my use case would look if I had class decorators: @IOClass class MyClass: ... Does that count? My decorator wouldn't need any arguments, because it looks inside the class for all the information it needs. [1] That's actually a general solution to Phillip's concern: the decorator can always look for attributes in the class (or the class's __dict__ if you don't want them inherited) for large amounts of information that wouldn't comfortably fit up the top. That's an extra degree of freedom that we don't have with functions. - - - - - [1] Actually I would probably give it one optional argument, the name to register under if different from the class name. -- Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | Carpe post meridiam! | Christchurch, New Zealand | (I'm not a morning person.) | greg.ewing@canterbury.ac.nz +--------------------------------------+
At 03:09 PM 3/30/2006 +1200, Greg Ewing wrote:
Well, here's how my use case would look if I had class decorators:
@IOClass class MyClass: ...
Does that count? My decorator wouldn't need any arguments, because it looks inside the class for all the information it needs. [1]
That's actually a general solution to Phillip's concern: the decorator can always look for attributes in the class (or the class's __dict__ if you don't want them inherited) for large amounts of information that wouldn't comfortably fit up the top. That's an extra degree of freedom that we don't have with functions.
I fail to see how this is an improvement. It sounds like the worst of all possible worlds to me -- *much* worse than the status quo. I use class decorators to declare metadata *about* attributes, so having to add a bunch of attributes for that *and* having to stick a funny hat on top of the class is definitely not an improvement for me. Are you actually *using* this IOClass thing, or is this just a hypothetical proposal?
Phillip J. Eby wrote:
Are you actually *using* this IOClass thing, or is this just a hypothetical proposal?
I'm using it. It's not hypothetical. Putting all the info I want in the decorator itself wouldn't be very nice in my case, or at least that's my opinion. One issue is that I'm also abusing a class statement to create a namespace for my extra info. Inserting that as a decorator argument obviously wouldn't be possible... -- Greg
[promted by Phillip Eby's post, but not in response so content snipped] I think we both want class decorators as a more fine grained substitute for __metaclass__ (fine grained as in declared per-class-instance instead of this-class-and-all-its-children). I can think of three ways class decorators are used: 1) register pattern, use a class attribute or two to stick the class in a lookup table and optionally delete the meta-attribs from the class 2) __init__ pattern, examine the class and maybe munge it based on attribs 3) __new__ pattern, consider the class a namespace and use the attribs to generate an essentially new class based on the attribs (the main difference between 2 and 3 is that the __new__ caseis a total tear down and rebuild where 2 tends towards tweaks on the parent) Classes have a unique property in that they are the easiest way to make little namespaces in python. Modules require lots of file clutter, functions as namespaces require digging into the internals, dicts as namespaces require more finger typing and don't support attribute access. It turns out I have two use cases for class decorators and didn't even know it. One is the 'register' pattern that started this thread. In that case I just want to move the metadata outside the class (the @register(db_id=20) case) and the rest of the class definition is honest to goodness overriding of a method or two from the parent class to change the behavior of its instances. The other pattern I hadn't thought of would be a '@namespace' decorator. A @namespace decorator would strip the attribs of all class-like qualities - it would strip the class of all descriptor magic (using descriptors, of course!). I just want a convenient bag to stick related items in. The first time I ever used metaclasses was to make PLY[1] (a SLR parser) use per-class namespaces for lexers and grammars instead of per-module. The metaclass chewed through all the class attributes and returned a class that shared no attributes with the original - each class was basically a config file. The decorator version would be spelled '@make_a_lexer_class' or '@make_a_grammar_class'. PEAK and Zope seem like they do a mix of __init__ and __new__, my current use cases are just 'notice' (I'm not a user, so feel free to correct). I like the func-like decorator syntax because I have just a bit of metadata that I'd like to move outside the class. PEAK/Zope sounds like they use classes as a mix of class and namespace. Their class decorator would return a hybrid class that has applied the namespace parts (post processing) to the class parts. A partly new class. I'd like that spelled: @tweak_this_class class MyClass: namespacey_stuff = ... def class_thing(self, foo): pass That leaves a class decorator behaving like a function decorator. It is a declaration that the final product (maybe a completely different thing as in the PLY case) is the result of the tweak_this_class function. Writing the above certainly helped me clear up my thoughts on the issue, I hope it hasn't gone and muddied it for anyone else :) -jackdied [1] http://savannah.nongnu.org/projects/ply/
At 11:09 PM 3/29/2006 -0500, Jack Diederich wrote:
I think we both want class decorators as a more fine grained substitute for __metaclass__ (fine grained as in declared per-class-instance instead of this-class-and-all-its-children). I can think of three ways class decorators are used:
1) register pattern, use a class attribute or two to stick the class in a lookup table and optionally delete the meta-attribs from the class 2) __init__ pattern, examine the class and maybe munge it based on attribs 3) __new__ pattern, consider the class a namespace and use the attribs to generate an essentially new class based on the attribs
My uses of class decorators don't fit any of your descriptions -- neither do Zope's for that matter, at least not the ones I'm familiar with. "implements(IFoo)" doesn't use any class attributes or delete meta attributes from the class. The whole point of using these decorators is to *not* interfere with the class by putting metadata in attributes. This is especially true for PEAK's class decorators, which are used to do things like make security declarations about attributes offered by the class (see the examples I posted previously in response to Fred's @class proposal).
It turns out I have two use cases for class decorators and didn't even know it. One is the 'register' pattern that started this thread. In that case I just want to move the metadata outside the class (the @register(db_id=20) case) and the rest of the class definition is honest to goodness overriding of a method or two from the parent class to change the behavior of its instances. The other pattern I hadn't thought of would be a '@namespace' decorator. A @namespace decorator would strip the attribs of all class-like qualities - it would strip the class of all descriptor magic (using descriptors, of course!). I just want a convenient bag to stick related items in.
This seems to me like a perfectly good place to use a metaclass. The place where metaclasses tend to quickly meet their downfall is in trying to support inheritance, especially multiple inheritance. If you just have a bag of data, however, inheritance doesn't enter into it and a metaclass is *perfect*. It doesn't even have to inherit from 'type', it can even be a classic class or a function, just as long as it takes a name, bases tuple, and dictionary.
PEAK and Zope seem like they do a mix of __init__ and __new__, my current use cases are just 'notice' (I'm not a user, so feel free to correct). I like the func-like decorator syntax because I have just a bit of metadata that I'd like to move outside the class. PEAK/Zope sounds like they use classes as a mix of class and namespace. Their class decorator would return a hybrid class that has applied the namespace parts (post processing) to the class parts. A partly new class.
I'd like that spelled: @tweak_this_class class MyClass: namespacey_stuff = ... def class_thing(self, foo): pass
You can do this quite easily now. Try running this code and see what happens: class Tweaker(object): def __class__(self, name, bases, cdict): print name, bases, cdict # put code here that returns something using name,bases,cdict return "I'm a string!" Tweak = Tweaker() class MyClass(Tweak): namespacey_stuff = "foo" def class_thing(self, foo): pass assert MyClass == "I'm a string!" If what you're making isn't a class, metaclasses work just fine. The return value of the __class__ method shown here will be bound to the name 'MyClass' when this runs.
That leaves a class decorator behaving like a function decorator.
They always did, and as far as I know, nobody has proposed they behave otherwise.
It is a declaration that the final product (maybe a completely different thing as in the PLY case) is the result of the tweak_this_class function.
Again, if you don't want a class to be the result, you can do this sort of thing quite easily now.
Jack Diederich wrote:
Classes have a unique property in that they are the easiest way to make little namespaces in python.
For a while now, I've been wondering whether it would be worth having a construct purely for creating little namespaces, instead of abusing a class for this. I've been thinking about an 'instance' statement that creates an instance of a class: instance my_thing(MyClass): # attribute assignments go here -- Greg
[Jack Diederich]
Classes have a unique property in that they are the easiest way to make little namespaces in python.
[Greg Ewing]
For a while now, I've been wondering whether it would be worth having a construct purely for creating little namespaces, instead of abusing a class for this.
FWIW, I do not consider it an abuse to use a class to create a small namespace. Essentially that is what it is for -- it matters not whether the class has no methods. What I've been wanting is the ability to easily define functions directly into an existing namespace: class A: pass def A.mymethod(x): return x+1 The latter definition is equivalent to: A.mymethod = lambda(x): x+1 This ability to inject function definitions into an existing namespace is the one missing piece to enable clean use of Protocol OO techniques: a = Object.copy() b = a.copy() def b.f(x): return x+1 c = b.copy() def c.f(x): return x+2 Raymond
Raymond Hettinger wrote:
FWIW, I do not consider it an abuse to use a class to create a small namespace. Essentially that is what it is for -- it matters not whether the class has no methods.
Two problems with that: * The word "class" in front of it is a misnomer if you've no intention of using it as a class. * It's not a completely blank slate -- it comes with various baggage in the form of __xxx__ attributes (my registration function has to filter those out). Plus some unwanted behaviour such as being callable, and doing magic things with attribute access when the attribute is a descriptor. -- Greg
python-dev-bounces+python=theyoungfamily.co.uk@python.org wrote on 30/03/2006 11:38:30:
Jack Diederich wrote:
Classes have a unique property in that they are the easiest way to make little namespaces in python.
For a while now, I've been wondering whether it would be worth having a construct purely for creating little namespaces, instead of abusing a class for this.
I've been thinking about an 'instance' statement that creates an instance of a class:
instance my_thing(MyClass):
# attribute assignments go here
Maybe this would be a use for the proposal a while back where: 'statement' name(args): ... implied name = 'statement'("name", args, namespace) then we could have: namespace foo: ... and interface IFoo(IBar): ... and your instance my_thing(MyClass): ... ? Cheers, Ben
-- Greg _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python- dev/python%40theyoungfamily.co.uk
Ben.Young@risk.sungard.com wrote:
python-dev-bounces+python=theyoungfamily.co.uk@python.org wrote on 30/03/2006 11:38:30:
Jack Diederich wrote:
Classes have a unique property in that they are the easiest way to make little namespaces in python.
For a while now, I've been wondering whether it would be worth having a construct purely for creating little namespaces, instead of abusing a class for this.
I've been thinking about an 'instance' statement that creates an instance of a class:
instance my_thing(MyClass):
# attribute assignments go here
Maybe this would be a use for the proposal a while back where:
'statement' name(args): ...
implied
name = 'statement'("name", args, namespace) [...]
I like that generalization (since a class definition statement currently does about the same anyway). However, please post that to the python-3000 list as this would be a change for Python 3. Cheers, Georg
Jack Diederich wrote:
Using metaclasses also required gross hacks like checking for a 'DO_NOT_REGISTER' member for subclasses that wanted to inherit from a class that had a Register metaclass but didn't want to be registered.
I've just done something like this myself in the last few days, and my solution was to have the registration metaclass look for an attribute in the class's __dict__, instead of using attribute access on the class, to decide whether to register it. That way the registerability doesn't get inherited, and I get to choose individually for each class. This of course may not be such a good idea if you want registerability to *usually* be inherited, but not always. In that case, you need some way of saying when you don't want it, in which case a do-not-register attribute doesn't seem so bad. How else would you provide this information? Still want class decorators, though. :-) BTW, the system I'm working on is a response to some hair-tearing problems I was experiencing trying to pickle some of my data, and also to address some disadvantages I see in using pickle for long-term data storage. I'll write something up about it if it works out... -- Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | Carpe post meridiam! | Christchurch, New Zealand | (I'm not a morning person.) | greg.ewing@canterbury.ac.nz +--------------------------------------+
Phillip J. Eby wrote:
My comment above was only about readable *placement* of the decorators, not the actual syntax.
The placement is part of the syntax... -- Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | Carpe post meridiam! | Christchurch, New Zealand | (I'm not a morning person.) | greg.ewing@canterbury.ac.nz +--------------------------------------+
participants (6)
-
Ben.Young@risk.sungard.com
-
Georg Brandl
-
Greg Ewing
-
Jack Diederich
-
Phillip J. Eby
-
Raymond Hettinger