Fix Python – How to decorate a class?

Question

Asked By – Robert Gowland

How do I create a decorator that applies to classes?

Specifically, I want to use a decorator addID to add a member __id to a class, and change the constructor __init__ to take an id argument for that member.

def getId(self): return self.__id

classdecorator addID(cls):
    def __init__(self, id, *args, **kws):
        self.__id = id
        self.getId = getId
        cls.__init__(self, *args, **kws)

@addID
class Foo:
    def __init__(self, value1):
        self.value1 = value1

The above should be equivalent to:

class Foo:
    def __init__(self, id, value1):
        self.__id = id
        self.value1 = value1

    def getId(self): return self.__id

Now we will see solution for issue: How to decorate a class?


Answer

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):
    pass

class 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.

This question is answered By – Jarret Hardie

This answer is collected from stackoverflow and reviewed by FixPython community admins, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0