[Python-3000] yes to class decorators

tomer filiba tomerfiliba at gmail.com
Thu Nov 16 00:06:04 CET 2006


i understand there's a green light for class decorators in py3k,
so i wanted to give the issue a slight push.

one problem with metaclasses is they are non-nestable, i.e., a
function can be wrapped by a number of decorators, while
classes can have only one metaclass.

not only that, but metaclasses complicate inheritance,
because their are not "componentized" the way decorators
are (i.e., decorator A does not impose restrictions on
how decorator B will work, whereas metaclasses must
be subclasses of one another, which means they are not
isolated *components*)

first i want to differentiate between ``metaclasses`` and
``pseudo metaclasses``. real metaclasses subclass type
and change it's behavior. pseudo metaclasses just use
the __metaclass__ magic syntax to modify the class
being returned, but not it's type (i.e., it's type is //type//)

for example -- this is what i call pseudo-metaclass:
>>> def singleton(*args):
...     return type(*args)() # create the only instance
...
>>> class OnlyOne(object):
...     __metaclass__ = singleton
...
>>> OnlyOne
<__main__.OnlyOne object at 0x009F5270>

of course we want to keep metaclasses as a facility as
well as a concept, but we do not want to have to use the
__metaclass__ syntax when it's not about real metaclasses.

class decorators -- like function decorators -- wrap the object
(class in this case), which makes them stackable and
componentized.

here are some usecases to class decorators:

def singleton(cls):
    return cls() # create the "single instance"

@singleton
class OnlyOne(object):
    pass

another example: @staticclass - automatically make all
methods staticmethods, or whatever (dotNET has them,
for instance). essentially this turns the class into a module.

def staticclass(cls):
    newdict = {}
    for name, obj in cls.__dict__.iteritems():
        if isinstance(getattr(cls, name), MethodType):
            newdict[name] = staticmethod(obj)
        else:
           newdict[name] = obj
    return type(cls.__name__, cls.__bases__, newdict)

@staticclass
class Eggs(objects):
    def foo():
        print "foo"
    def goo():
        print "goo"

Eggs.foo()
e = Eggs()
e.goo()

i'm not saying this particular example is useful, but i have
had many times when i thought "argh, i wish i had class
decorators for that".

sadly though, i don't remember many of them, since i always
had to find workarounds :)

ANYWAY, if we went this far already, we might as well just
trash the __metaclass__ syntax (*) altogether. we can easily
implement a metaclass decorator that does the trick:

def metaclass(meta):
    def wrapper(cls):
        return meta(cls.__name__, cls.__bases__, dict(cls.__dict__))
    return wrapper

class FooMeta(type):
   ....

@singleton
@metaclass(FooMeta)
class Bar(object):
    ....

or even something like

@FooMeta
class Bar(object):
    pass

although that would require some hackery. forget it, it's better
to be explicit.

(*) throwing only the syntax. subclassing type is still required
of course, but IMHO it's much more clear to understand where
the "magic" comes from when a class has a @metaclass
decorator instead of a __metaclass__ attribute.

moreover, as shown above, we can simplify the way type_new()
works, which is a blessing on its own (considering its current
complexity)


- tomer


More information about the Python-3000 mailing list