Attaching a Decorator to All Functions Within a Class

Attaching a decorator to all functions within a class

The cleanest way to do this, or to do other modifications to a class definition, is to define a metaclass.

Alternatively, just apply your decorator at the end of the class definition using inspect:

import inspect

class Something:
def foo(self):
pass

for name, fn in inspect.getmembers(Something, inspect.isfunction):
setattr(Something, name, decorator(fn))

In practice of course you'll want to apply your decorator more selectively. As soon as you want to decorate all but one method you'll discover that it is easier and more flexible just to use the decorator syntax in the traditional way.

How to decorate all functions of a class without typing it over and over for each method?

Decorate the class with a function that walks through the class's attributes and decorates callables. This may be the wrong thing to do if you have class variables that may happen to be callable, and will also decorate nested classes (credits to Sven Marnach for pointing this out) but generally it's a rather clean and simple solution. Example implementation (note that this will not exclude special methods (__init__ etc.), which may or may not be desired):

def for_all_methods(decorator):
def decorate(cls):
for attr in cls.__dict__: # there's propably a better way to do this
if callable(getattr(cls, attr)):
setattr(cls, attr, decorator(getattr(cls, attr)))
return cls
return decorate

Use like this:

@for_all_methods(mydecorator)
class C(object):
def m1(self): pass
def m2(self, x): pass
...

In Python 3.0 and 3.1, callable does not exist. It existed since forever in Python 2.x and is back in Python 3.2 as wrapper for isinstance(x, collections.Callable), so you can use that (or define your own callable replacement using this) in those versions.

How to decorate all functions and class methods in python?

Find this solution.
It requires 2 modules:

"""module2 module : wrapper to modify object"""

import sys
import importlib

_hackers = []
def register(obj):
_hackers.append(obj)

class Hacker:
def hack(self, module):
return module

class Loader:
def __init__(self):
self.module = None

def find_module(self, name, path):
sys.meta_path.remove(self)
self.module = importlib.import_module(name)
sys.meta_path.insert(0, self)
return self

def load_module(self, name):
if not self.module:
raise ImportError("Unable to load module.")
module = self.module
for hacker in _hackers:
module = hacker.hack(module)
sys.modules[name] = module
return module

sys.meta_path.insert(0, Loader())

"""decorations module"""

import types
import module2

_modules = {}
_decorators = []
def register(decorator, modules=None):
"""Register a decorator for a list of module names."""
if not decorator:
return
if not modules and decorator in _decorators:
return
if not modules:
_decorators.append(decorator)
return

if isinstance(modules, str):
modules = (modules,)
for module in modules:
if module not in _modules:
_modules[module] = []
_modules[module].append(decorator)

class One(module2.Hacker):
def hack(self, module):
for decorator in _modules.get(module.__name__, ()):
self.decorate(module, decorator)
for decorator in _decorators:
self.decorate(module, decorator)
return module
def decorate(self, module, decorator):
for attr in module.__dict__:
obj = getattr(module, attr)
if isinstance(obj, types.FunctionType):
setattr(module, attr, decorator(obj))

module2.register(One())

How to decorate all methods in a class? Can I just decorate the class?

Your can start all method that required to be decorated with some prefix and then use something like this:

class Xobject(object):

def __init__(self, decorator):
for method_name in dir(self):
if method_name.startswith("dec_"):
attr = getattr(self, method_name)
wrapped = decorator(attr)
setattr(self, method_name, wrapped)

def dec_me_1(self):
print("In dec_me1")
return 0

def dec_me_2(self):
print("In dec_me2")
return 1

def decorator(func):

def wrapped(*args):
print("TEST")
return func(*args)

return wrapped

x = Xobject(decorator)

x.dec_me_1()
x.dec_me_2()

UPDATE:

You can decorate class by mean of function below. When using Python you should know that class in Python is also object so you could change it and pass it to the other function.

def decorator(func):

def wrapped(*args):
print("TEST")
return func(*args)

return wrapped

def decorate_object(p_object, decorator):
for method_name in dir(p_object):
if method_name.startswith("dec_"):
attr = getattr(p_object, method_name)
wrapped = decorator(attr)
setattr(p_object, method_name, wrapped)

decorate_object(Xobject, decorator)

x = Xobject()

x.dec_me_1()
x.dec_me_2()

Also your can decorate already instantiated object same way:

x = Xobject()

x.dec_me_1()
x.dec_me_2()

decorate_object(x, decorator)

x.dec_me_1()
x.dec_me_2()

How to apply a decorator to all class methods using class decorator

This is possible by overriding class methods

function AttachToAllClassDecorator<T>(someParam: string) {
return function(target: new (...params: any[]) => T) {
for (const key of Object.getOwnPropertyNames(target.prototype)) {
// maybe blacklist methods here
let descriptor = Object.getOwnPropertyDescriptor(target.prototype, key);
if (descriptor) {
descriptor = someDecorator(someParam)(key, descriptor);
Object.defineProperty(target.prototype, key, descriptor);
}
}
}
}

Basically going through all methods (maybe add some logic around it for whitelisting/blacklisting some methods) and overriding with new method that has method decorator wrapped.

Here is basic example for method decorator.

function someDecorator(someParam: string): (methodName: string, descriptor: PropertyDescriptor) => PropertyDescriptor {
return (methodName: string, descriptor: PropertyDescriptor): PropertyDescriptor => {
let method = descriptor.value;

descriptor.value = function(...args: any[]) {
console.warn(`Here for descriptor ${methodName} with param ${someParam}`);

return method.apply(this, args);
}

return descriptor;
}
}

TS Playground

Python decorators in classes

Would something like this do what you need?

class Test(object):
def _decorator(foo):
def magic( self ) :
print "start magic"
foo( self )
print "end magic"
return magic

@_decorator
def bar( self ) :
print "normal call"

test = Test()

test.bar()

This avoids the call to self to access the decorator and leaves it hidden in the class namespace as a regular method.

>>> import stackoverflow
>>> test = stackoverflow.Test()
>>> test.bar()
start magic
normal call
end magic
>>>

edited to answer question in comments:

How to use the hidden decorator in another class

class Test(object):
def _decorator(foo):
def magic( self ) :
print "start magic"
foo( self )
print "end magic"
return magic

@_decorator
def bar( self ) :
print "normal call"

_decorator = staticmethod( _decorator )

class TestB( Test ):
@Test._decorator
def bar( self ):
print "override bar in"
super( TestB, self ).bar()
print "override bar out"

print "Normal:"
test = Test()
test.bar()
print

print "Inherited:"
b = TestB()
b.bar()
print

Output:

Normal:
start magic
normal call
end magic

Inherited:
start magic
override bar in
start magic
normal call
end magic
override bar out
end magic

How can one attach a decorator to a function after the fact in python?

You imported sqrt into your module, just apply the decorator there in your own global namespace:

sqrt = print_args_decor(sqrt)

This sets the name sqrt in your module namespace to the result of the decorator. There is no requirement that sqrt was originally defined in this module.

It is up to the decorator to uses the functools.wraps() decorator to preserve function metadata such as the name and docstring.

Decorating a class is no different in this respect:

ClassName = decorator(ClassName)

On Python 2, for methods you need to be careful to grab the original unbound function; easiest is to use the method.__func__ attribute:

try:
# Python 2
ClassName.function_name = decorator(ClassName.function_name.__func__)
except AttributeError:
# Python 3
ClassName.function_name = decorator(ClassName.function_name)

I've wrapped the above in a try...except to make the pattern work across Python versions. The alternative is to grab the function object out of the class __dict__ to avoid the descriptor protocol from kicking in:

ClassName.function_name = decorator(ClassName.__dict__['function_name'])

Can a decorator of an instance method access the class?

If you are using Python 2.6 or later you could use a class decorator, perhaps something like this (warning: untested code).

def class_decorator(cls):
for name, method in cls.__dict__.iteritems():
if hasattr(method, "use_class"):
# do something with the method and class
print name, cls
return cls

def method_decorator(view):
# mark the method as something that requires view's class
view.use_class = True
return view

@class_decorator
class ModelA(object):
@method_decorator
def a_method(self):
# do some stuff
pass

The method decorator marks the method as one that is of interest by adding a "use_class" attribute - functions and methods are also objects, so you can attach additional metadata to them.

After the class has been created the class decorator then goes through all the methods and does whatever is needed on the methods that have been marked.

If you want all the methods to be affected then you could leave out the method decorator and just use the class decorator.

Apply a default decorator to all methods on a class, and exclude them selectively

I'm a little confused with what you're trying to achieve. If you want to have lazy loading why don't you have the functions that require the data call on a property that's only evaluated the first time?

class myClass
_things = []
params = []

def __init__(self, params):
self.params = params

@property
def things(self):
if not self._things:
self._things = do_a_ton_stuff()
return self._things

def get_things():
return self.things

def get_some_things():
return apply_a_filter(self.things)

def get_first_thing():
return self.things[0]

def get_last_thing():
return self.things[-1]

Some people make a @cachedproperty decorator for this sort of pattern - so they don't have to do the self._things bit themselves. http://code.activestate.com/recipes/576563-cached-property/



Related Topics



Leave a reply



Submit