How to decorate all functions of a class without typing it over and over for each method?
Solution 1
While I'm not fond of using magical approaches when an explicit approach would do, you can probably use a metaclass for this.
def myDecorator(fn):
fn.foo = 'bar'
return fn
class myMetaClass(type):
def __new__(cls, name, bases, local):
for attr in local:
value = local[attr]
if callable(value):
local[attr] = myDecorator(value)
return type.__new__(cls, name, bases, local)
class myClass(object):
__metaclass__ = myMetaClass
def baz(self):
print self.baz.foo
and it works as though each callable in myClass
had been decorated with myDecorator
>>> quux = myClass()
>>> quux.baz()
bar
Solution 2
Not to revive things from the dead, but I really liked delnan's answer, but found it sllliigghhtttlllyy lacking.
def for_all_methods(exclude, decorator):
def decorate(cls):
for attr in cls.__dict__:
if callable(getattr(cls, attr)) and attr not in exclude:
setattr(cls, attr, decorator(getattr(cls, attr)))
return cls
return decorate
EDIT: fix indenting
So you can specify methods//attributes//stuff you don't want decorated
Solution 3
None of the above answers worked for me, since I wanted to also decorate the inherited methods, which was not accomplished by using __dict__
, and I did not want to overcomplicate things with metaclasses. Lastly, I am fine with having a solution for Python 2, since I just have an immediate need to add some profiling code for measuring time used by all functions of a class.
import inspect
def for_all_methods(decorator):
def decorate(cls):
for name, fn in inspect.getmembers(cls, inspect.ismethod):
setattr(cls, name, decorator(fn))
return cls
return decorate
Source (slightly different solution): https://stackoverflow.com/a/3467879/1243926 There you can also see how to change it for Python 3.
As comments to other answers suggest, consider using inspect.getmembers(cls, inspect.isroutine)
instead. If you have found a proper solution that works for both Python 2 and Python 3 and decorates inherited methods, and can still be done in 7 lines, please, edit.
Solution 4
You could generate a metaclass. This will not decorate inherited methods.
def decorating_meta(decorator):
class DecoratingMetaclass(type):
def __new__(self, class_name, bases, namespace):
for key, value in list(namespace.items()):
if callable(value):
namespace[key] = decorator(value)
return type.__new__(self, class_name, bases, namespace)
return DecoratingMetaclass
This will generate a metaclass decorating all methods with the specified function. You can use it in Python 2 or 3 by doing something like this
def doubling_decorator(f):
def decorated(*a, **kw):
return f(*a, **kw) * 2
return decorated
class Foo(dict):
__metaclass__ = decorating_meta(doubling_decorator)
def lookup(self, key):
return self[key]
d = Foo()
d["bar"] = 5
print(d.lookup("bar")) # prints 10
Comments
-
rapadura over 4 years
Lets say my class has many methods, and I want to apply my decorator on each one of them, later when I add new methods, I want the same decorator to be applied, but I dont want to write @mydecorator above the method declaration all the time?
If I look into
__call__
is that the right way to go?IMPORTANT: the example below appears to be solving a different problem than the original question asked about.
EDIT: Id like to show this way, which is a similar solution to my problem for anyobody finding this question later, using a mixin as mentioned in the comments.
class WrapinMixin(object): def __call__(self, hey, you, *args): print 'entering', hey, you, repr(args) try: ret = getattr(self, hey)(you, *args) return ret except: ret = str(e) raise finally: print 'leaving', hey, repr(ret)
Then you can in another
class Wrapmymethodsaround(WrapinMixin): def __call__: return super(Wrapmymethodsaround, self).__call__(hey, you, *args)
-
rapadura about 13 yearsThats also interesting, thanks!
-
rapadura about 13 yearsCan you comment on the mixin approach?
-
nickneedsaname over 12 yearsactually you can really go nuts. You can have an include instead of an exclude (but not both because that doesn't make sense...) and stuff. But this is pretty powerful magic here.
-
rapadura over 12 yearsImprovements are always welcome, someone may find it useful with time
-
nickneedsaname over 12 yearsThat's what I was hoping for! Thanks for the vote of confidence :P
-
Kyle Pittman about 8 yearsJust a caveat:
callable(<class>)
isTrue
. This behavior may or may not be desired, depending upon your use case. -
Gregory Kuhn almost 8 yearsI am also looking into this approach and it's great. Here is a good reference discussing metaclasses in general: blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses One good point mentioned within this reference is that this approach also takes care of subclasses where as, I believe that class decorators do not. This, to me is an important point!
-
jhrr over 6 yearsI don't think this is magical at all, rather that metaclasses have a bad PR department. They should be used whenever they are suitable, as is the case here. Good solution!
-
davegallant about 6 yearsI like this. maybe exclude should default to None though. :D
-
TheEagle almost 3 years@davegallant
"maybe exclude should default to None though"
- that would not be a good idea -TypeError: NoneType object is not iterable
… -
davegallant almost 3 yearsI'm trying to remember why I wrote this. I think I probably meant an empty list [].
-
NoamG about 2 yearsNotice that for python 3 you should use:
class myClass(metaclass=myMetaClass):