Python Decorators

Decorators are quite a useful Python feature. However, it can seem that any resources or insights surrounding them makes the whole concept impossible to understand. But decorators are, in fact, quite simple. Read on, and we’ll show you why.

Why do I need a decorator?

Let’s start by ignoring any Python or software for a moment, and instead illustrate the concept using a real life scenario.

Very early in life we learn to move by walking. Later in life, we may learn to move by riding a bicycle. And driving a car. And perhaps riding a skateboard. But whichever way we learn, we are still just moving, the same as we always have.

To understand the decorator concept, imagine that walking, riding a bicycle, driving a car, and riding a skateboard are all behaviors that augment moving: they are decorating the move behavior.

In short, this is the exact concept of the decorator!

“Cycling” is a behavior that “decorates” the way something, in this case, a person, moves. Walking, driving, and riding a bicycle all represent alternative ways of moving that can be applied not only to the behaviors of a person, but also to other applicable objects. (A dog, for instance, could walk, and possibly ride a skateboard. I’m not sure he could get a driver’s license though!)

So, now that we have described the concepts, let’s take a look at some Python:

>>> def calculate_amount(premium, rate):
...     return rate * premium
...
>>>

This is a simple function that calculates an amount after applying interest. And we use it in various applications to calculate the impact of interest. For example, this way:

>>> total = calculate_amount(120, 1.10)
>>> total
132.0
>>>

Now, we’re going to implement a self-service web application that allows our customers to submit loan requests. This web application will use the same interest function. However, since it’s going to be used by customers rather than our own customer service reps, we need to log the results of the calculation to have an audit trail of the customers’ own calculations.

Note that the calculation and the business logic are identical in either case. However, we want to use a technical feature – logging – to address a supplemental business requirement. A good design allows us to decouple differing concepts, especially those that relate to our business logic vs. the technology we use. It also accounts for concepts that change at different time.

Consider that a change in technology, such as an upgraded third-party component, might have us upgrade our logging logic. We want to avoid having to have to touch the business logic: it increases the likelihood of us breaking something, which may result in additional testing. These extra steps would increase implementation time and risk.

This is where decorators get to shine! A decorator embellishes our business logic without changing it, and mitigates the risks discussed above. Not only that, but it allows us to selectively use it to only log things we really need to log – and do so easily. This way, unnecessary logging that could slow down performance is eliminated.

This is why we are going to use a decorator, rather than develop, say, a log_calculate_amount function.

Next, let’s walk through the thought process for designing a solution that enables logging.

Back to Top