Advanced Usage

Note

It is possible to decorate static and class methods but you have to ensure that the order of decorators is right. The @staticmethod and @classmethod decorators should always be on top.:

@staticmethod
@my_decorator
def my_static_method():
    pass

Warning

The fully qualified name of a method cannot be properly determined for static methods and class methods.

Warning

The fully qualified name of a method or function cannot be properly determined in case they are already decorated. This only applies to decorators that aren’t using Decorator

Decorators can be used in three different ways.:

@my_decorator
@my_decorator()
@my_decorator(my_argument=True)
def my_function():
   pass

Decorators can be used on all callables so you can decorator functions, (new style) classes and methods.:

@my_decorator
def my_function():
    pass

@my_decorator
class MyClass(object):
    @my_decorator
    def my_method():
        pass

Combining decorators

Combining decorators is as simple as just stacking them on a function definition.

Important

The before() and after() methods of the decorators are in different orders. In the example below the before() methods of step2 and step1 are executed and then the method itself. The after() method is called for step1 and then step2 after the method is executed. So the call stack becomes

  • step2.before()
  • step1.before()
  • my_process()
  • step1.after()
  • step2.after()
@step2
@step1
def my_process():
    pass

If you want to create a decorator that combines decorators you can do that like this:

class process(decorators.Decorator):
    """Combines step1 and step2 in a single decorator"""

    def before(self):
        self.func = step2(step1(self.func))

Non-keyworded decorators

Although not supported out of the box, it is possible to create decorators with non-keyworded or positional arguments:

import fqn_decorators

class arg_decorator(fqn_decorators.Decorator):

    def __init__(self, func=None, *args, **kwargs):
        self._args = args
        super(arg_decorator, self).__init__(func, **kwargs)

    def __call__(self, *args, **kwargs):
        if not self.func:
            # Decorator initialized without providing the function
            return self.__class__(args[0], *self._args, **self.params)
        return super(arg_decorator, self).__call__(*args, **kwargs)

    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type), *self._args, **self.params)

    def before(self):
        print self._args


@arg_decorator(None, 1, 2)
def my_function():
    pass

>>>my_function()
(1, 2)