Decorators! Decorators are awesome.
You can create them as classes or function closures, depending on the required features and level of complexity, but the basic concept is the same.
def my_handler(func):
def wrapper(*args, **kwargs):
print 'pre function call'
results = func(*args, **kwargs)
print 'post function call'
return results
return wrapper
@my_handler
def my_func(arg, kwarg='yes'):
print 'in my func'
print arg, kwarg
The @my_handler tells python that the following function should be decorated by my_handler. It’s just syntactic sugar for:
def my_func(arg, kwarg='yes'):
print 'in my func'
print arg, kwarg
my_func = my_handler(my_func)
my_handler is passed the function object that it is decorating as an argument, and my_handler then returns a function (wrapper, in this case) to be run in it’s place.
Class-based decorators return an instance of the decorator class and thus use the call method instead of returning a decorated function. The same decorator as a class:
class my_handler(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print 'pre function call'
results = self.func(*args, **kwargs)
print 'post function call'
return results
There are a lot of resources on decorators, but I found this (2-part article) be a good explanation of them: Decorators I: Introduction to Python Decorators
Part II, which goes over creating decorators that accept arguments: Python Decorators II: Decorator Arguments
Also Since a different function is being returned in place of the original, the function will lose all of it’s original metadata, so if you want to retain things like name and doc you have a add them in the decorator:
class my_handler(object):
def __init__(self, func):
self.func = func
self.__doc__ = func.__doc__
self.__name__ = func.__name__
self.__module__ = func.__module__
def __call__(self, *args, **kwargs):
# ...