[Python-3000] PEP Draft: Class Decorators (original) (raw)
Collin Winter collinw at gmail.com
Sat Mar 10 01:19:49 CET 2007
- Previous message: [Python-3000] PEP Draft: Class Decorators
- Next message: [Python-3000] PEP Draft: Class Decorators
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
You need to run this through a spellchecker; I noticed at least a dozen typos. Other comments inline.
Collin Winter
On 3/9/07, Jack Diederich <jackdied at jackdied.com> wrote:
PEP: 3XXX Title: Class Decorators Version: 1 Last-Modified: 28-Feb-2007 Authors: Jack Diederich Implementation: SF#1671208 Status: Draft Type: Standards Track Created: 26-Feb-2007
Abstract ======== Extending the decorator syntax to allow the decoration of classes. Rationale ========= Class decorators have an identical signature to function decorators. The identity decorator is defined as def identity(cls): return cls @identity class Example: pass To be useful class decorators should return a class-like thing but as with function decorators this is not enforced by the language. All the strong existing use cases are decorators that just register or annotate classes. import myfactory @myfactory.register class MyClass: pass Decorators are stackable so more than one registration can happen. import myfactory import mycron @mycron.schedule('nightly') @myfactory.register class MyClass: pass The same effect is currently possible by making a functioon call after class definition time but as with function decorators decorating the class moves an important piece of information (i.e. 'this class participates in a factory') to the top.
"but as with function decorators decorating" -> "but, as with function decorators, decorating" (adding commas makes it easier to parse).
Decorators vs Metaclasses =========================
Decorators are executed once for each time they appear. Metaclasses are executed for every class that defines a metaclass and every class that inherits from a class that defines a metaclass. Decorators are also easilly stackable because of their takes-a-class returns-a-class signature. Metaclasses are combinable too but with their richer interface it requires more trouble (see Alex Martelli's PyCon 2005 talk[6]). Even with some tricks the combined metaclasses may need to coordinate who owns new or init. Note that class decorators, like metaclasses, aren't garunteed to return the same class they were passed. History and Implementation ========================== Class decorators were originally proposed alongside function decorators in PEP318 [1] and were rejected by Guido [2] for lack of use cases. Two years later he saw a use case he liked and gave the go-ahead for a PEP and patch [3].
I'd still prefer it if this use case -- which is so central to the PEP -- were provided in the PEP itself.
The current patch is loosely based on a pre-2.4 patch [4] and updated to use the newer 2.5 (and 3k) AST.
Grammar/Grammar is changed from funcdef: [decorators] 'def' NAME parameters ['->' test] ':' suite to decoratedthing: decorators (classdef | funcdef) funcdef: 'def' NAME parameters ['->' test] ':' suite "decoratedthing"s are premitted everywhere that funcdef and classdef are premitted. An alternate change to the grammar would be to make a 'decoratablething' which would include funcdefs and classdefs even if they had no decorators.
Is it "decorated_thing" or "decoratable_thing"?
Motivation ==========
Below is an actual production metaclass followed by its class decorator equivalent. It is used to produce factory classes which each keep track of which accountid web forms are associated with. class Register(type): """A tiny metaclass to help classes register themselves automagically""" def init(cls, name, bases, dict): if 'DONOTREGISTER' in dict: # this is a non-concrete class pass elif object not in bases: # this is yet another metaclass pass elif 'register' in dict: # this is a top level factory class setattr(cls, 'register', staticmethod(dict['register']))p else: # typical case, register with the factory cls.register(name, cls) return class FormFactory(object): idtoform = {} # { accountid : { formid : formclass } } def register(cls): FormFactory.idtoform.setdefault(cls.accountid, {}) assert cls.formid not in FormFactory.idtoform[cls.accountid], cls.formid FormFactory.idtoform[cls.accountid][cls.formid] = cls class FormCaputreA(FormFactory, Form): accountid = 17 formid = 3 # .. cgi param to sql mapping defined, etc The decorated version eliminates the metaclass and loses some of the if-else checks because it won't be applied by the programmer to intermediate classes, metaclasses, or factories. class FormFactory(Form): idtoform = {} # { accountid : { formid : formclass } } @staticmethod def register(cls, accountid, formid): FormFactory.idtoform.setdefault(cls.accountid, {}) assert formid not in FormFactory.idtoform[cls.accountid], formid FormFactory.idtoform[cls.accountid][formid] = cls # return the class unchanged return cls @FormFactory.register(accountid=17, formid=3) class FormCaptureA(object): # .. cgi param to sql mapping defined, etc References ========== If you enjoyed this PEP you might also enjoy: .. [1] PEP 318, "Decorators for Functions and Methods" http://www.python.org/dev/peps/pep-0318/ .. [2] Class decorators rejection http://mail.python.org/pipermail/python-dev/2004-March/043458.html .. [3] Class decorator go-ahead http://mail.python.org/pipermail/python-dev/2006-March/062942.html .. [4] 2.4 class decorator patch http://python.org/sf/1007991 .. [5] 3.x class decorator patch http://python.org/sf/1671208 .. [6] Alex Martelli's PyCon 2005 "Python's Black Magic" http://www.python.org/pycon/2005/papers/36/
- Previous message: [Python-3000] PEP Draft: Class Decorators
- Next message: [Python-3000] PEP Draft: Class Decorators
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]