Cool ideas revolving around computers and programming

Main menu

Post navigation

Python decorator mini-study (part 2 of 3)

Introduction

In the previous part of this article I introduced the Params2attribs decorator that copies object initialiser parameters to object attributes.

In this part I aim to introduce the Python decorator mechanisms needed to drill down into Params2attribs in the forthcoming third part of this series.

Python decorator 101

Python decorators come in two flavours: simple and not so simple :-) The Params2attribs decorator which is the subject of this article series falls into the second category. But, as they say, first things first..

Simple decorators

The simple decorator variety is in essence a function that returns a wrapper function that is to be invoked instead of the wrapped function. Confused? You should be :-)

Let’s have a look at an example. In the code below the wrapper function (lines 13 – 16) merely logs the invocation of the wrapped function.

Just in case you haven’t seen this before: the *args and **kwargs constructs (on lines 13 and 15) are the Python way to pass positional and keyword function parameters in a generic fashion.

is invoked by Python whenever it loads any code decorated with it (lines 19 – 20)

defines (lines 13 – 16) and returns (line 17) another function (wrapper()) that is to be invoked (line 23) instead of the decorated one (plf())

As you may have guessed already, running the code above results in the following output:

mhr@playground2:~/src/published$ python simpledeco.py
Hey plf(), you have been wrapped..
Help! I am only a poor little function!
.. resistance is futile

Complex decorators

Complex decorators may be implemented as classes or using nested functions. The latter variant was suggested by a reader and is shown after the section on decorator classes.

Decorator classes

When using decorator classes any data beyond the function to be wrapped is passed to the decorator class initialiser (see e.g. the before and after parameters of initialiser interceptor::__init__(), lines 13 – 15).

Nested functions

As a reader pointed out after the first draft of this article was posted (see also the first two comments on this weblog entry) a similar effect can be achieved using nested functions. The nested functions variant of the decorator looks as follows:

What is actually happening is that _calling_ the interceptor function is returning the decorator to use, which is then called as per the normal decorator application. The same thing as is happening with the class version, where calling creates and returns an instance, which is then used as the decorator, and called.