How to decorate a class?
Rise to the top 3% as a developer or hire one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------
Music by Eric Matyas
https://www.soundimage.org
Track title: The Builders
--
Chapters
00:00 How To Decorate A Class?
00:31 Answer 1 Score 13
01:07 Accepted Answer Score 89
01:58 Answer 3 Score 246
02:45 Answer 4 Score 36
03:25 Thank you
--
Full question
https://stackoverflow.com/questions/6819...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#python #decorator #python25
#avk47
ANSWER 1
Score 246
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:
@addID
class Foo:
pass
In older versions, you can do it another way:
class Foo:
pass
Foo = 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__.
ACCEPTED ANSWER
Score 89
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.
ANSWER 3
Score 36
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:
pass
FooId = addId(Foo)
And in Python 3 like this (but be careful to use super() in your classes):
@addId
class Foo:
pass
So you can have your cake and eat it - inheritance and decorators!
ANSWER 4
Score 13
That's not a good practice and there is no mechanism to do that because of that. The right way to accomplish what you want is inheritance.
Take a look into the class documentation.
A little example:
class Employee(object):
def __init__(self, age, sex, siblings=0):
self.age = age
self.sex = sex
self.siblings = siblings
def born_on(self):
today = datetime.date.today()
return today - datetime.timedelta(days=self.age*365)
class Boss(Employee):
def __init__(self, age, sex, siblings=0, bonus=0):
self.bonus = bonus
Employee.__init__(self, age, sex, siblings)
This way Boss has everything Employee has, with also his own __init__ method and own members.