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!