I have an object hierarchy in which almost all of the methods are class methods. It looks like the following:
class ParentObject(object):
def __init__(self):
pass
@classmethod
def smile_warmly(cls, the_method):
def wrapper(kls, *args, **kwargs):
print "-smile_warmly - "+kls.__name__
the_method(*args, **kwargs)
return wrapper
@classmethod
def greetings(cls):
print "greetings"
class SonObject(ParentObject):
@classmethod
def hello_son(cls):
print "hello son"
@classmethod
def goodbye(cls):
print "goodbye son"
class DaughterObject(ParentObject):
@classmethod
def hello_daughter(cls):
print "hello daughter"
@classmethod
def goodbye(cls):
print "goodbye daughter"
if __name__ == '__main__':
son = SonObject()
son.greetings()
son.hello_son()
son.goodbye()
daughter = DaughterObject()
daughter.greetings()
daughter.hello_daughter()
daughter.goodbye()
The code as given outputs the following:
greetings
hello son
goodbye son
greetings
hello daughter
goodbye daughter
I would like the code to output the following:
-smile_warmly - SonObject
greetings
-smile_warmly - SonObject
hello son
-smile_warmly - SonObject
goodbye son
-smile_warmly - DaughterObject
greetings
-smile_warmly - DaughterObject
hello daughter
-smile_warmly - DaughterObject
goodbye daughter
But I don't want to add the line @smile_warmly
before each method (and when I try to do that in the code above, I get error message TypeError: 'classmethod' object is not callable
). Rather, I would like the decoration of each method to take place programmatically in the __init__()
method.
Is it possible to programmatically decorate methods in Python?
EDIT: Found something that seems to work -- see my answer below. Thanks to BrenBarn.
All a decorator does is return a new function. This:
@deco
def foo():
# blah
is the same as this:
def foo():
# blah
foo = deco(foo)
You can do the same thing whenever you like, without the @
syntax, just by replacing functions with whatever you like. So in __init__
or wherever else, you could loop through all the methods and for each one replace it with smilewarmly(meth)
.
However, instead of doing it in __init__
, it would make more sense to do it when the class is created. You could do this with a metaclass, or more simply with a class decorator:
def smileDeco(func):
def wrapped(*args, **kw):
print ":-)"
func(*args, **kw)
return classmethod(wrapped)
def makeSmiley(cls):
for attr, val in cls.__dict__.iteritems():
if callable(val) and not attr.startswith("__"):
setattr(cls, attr, smileDeco(val))
return cls
@makeSmiley
class Foo(object):
def sayStuff(self):
print "Blah blah"
>>> Foo().sayStuff()
:-)
Blah blah
In this example I put the classmethod decoration inside my smileDeco
decorator. You could also put it in makeSmiley
so that makeSmiley
returned smileDeco(classmethod(val))
. (Which way you want to do it depends on how closely linked the smile-decorator is to the things being classmethods.) This means you don't have to use @classmethod
inside the class.
Also, of course, in the loop in makeSmiley
you can include whatever logic you like to decide (for instance, based on the method's name) whether to wrap it with the smile behavior or not.
Note that you'd have to be a little more careful if you really want to manually use @classmethod
inside the class, because classmethods as accessed via the class __dict__
aren't callable. So you'd have to specifically check whether the object is a classmethod object, instead of just checking whether it's callable.