How to decorate a class? How to decorate a class? python python

How to decorate a class?


Apart from the question whether class decorators are the right solution to your problem:

In Python 2.6 and higher, there are class decorators with the @-syntax, so you can write:

@addIDclass Foo:    pass

In older versions, you can do it another way:

class Foo:    passFoo = addID(Foo)

Note however that this works the same as for function decorators, and that the decorator should return the new (or modified original) class, which is not what you're doing in the example. The addID decorator would look like this:

def addID(original_class):    orig_init = original_class.__init__    # Make copy of original __init__, so we can call it without recursion    def __init__(self, id, *args, **kws):        self.__id = id        self.getId = getId        orig_init(self, *args, **kws) # Call the original __init__    original_class.__init__ = __init__ # Set the class' __init__ to the new one    return original_class

You could then use the appropriate syntax for your Python version as described above.

But I agree with others that inheritance is better suited if you want to override __init__.


I would second the notion that you may wish to consider a subclass instead of the approach you've outlined. However, not knowing your specific scenario, YMMV :-)

What you're thinking of is a metaclass. The __new__ function in a metaclass is passed the full proposed definition of the class, which it can then rewrite before the class is created. You can, at that time, sub out the constructor for a new one.

Example:

def substitute_init(self, id, *args, **kwargs):    passclass FooMeta(type):    def __new__(cls, name, bases, attrs):        attrs['__init__'] = substitute_init        return super(FooMeta, cls).__new__(cls, name, bases, attrs)class Foo(object):    __metaclass__ = FooMeta    def __init__(self, value1):        pass

Replacing the constructor is perhaps a bit dramatic, but the language does provide support for this kind of deep introspection and dynamic modification.


No one has explained that you can dynamically define classes. So you can have a decorator that defines (and returns) a subclass:

def addId(cls):    class AddId(cls):        def __init__(self, id, *args, **kargs):            super(AddId, self).__init__(*args, **kargs)            self.__id = id        def getId(self):            return self.__id    return AddId

Which can be used in Python 2 (the comment from Blckknght which explains why you should continue to do this in 2.6+) like this:

class Foo:    passFooId = addId(Foo)

And in Python 3 like this (but be careful to use super() in your classes):

@addIdclass Foo:    pass

So you can have your cake and eat it - inheritance and decorators!