Three Categories of Python Decorators

This article will not make sense to all Python developers.

It only makes sense if you know how to write decorators in Python. That is not a skill for beginners. I wouldn’t even say intermediate, if you’re doing it right.

That’s why only 1% of all Python developers will ever learn to write them, by my calculations.

It’s a shame. The major libraries in the Python world (Django, Flask, Pytest, SQLAlchemy and more) use decorators EXTENSIVELY. So when you’re in this top 1%, you can start building massively powerful libraries yourself.

If you’re not already in this elite 1%, there’s a way to get there. But first, I’ll reveal my taxonomy of decorators.

What is a decorator? It is a way of adding behavior. around a group of functions or methods.

Every part of that definition is important. Read this entire article 10 times, if necessary, until you understand it.

You apply a decorator to a function (or method). Actually, a bunch of them. The result is called a “decorated function”. (Or method. Just add “or method” whenever you mention a function.)

“Behavior” means “lines of code”. Code that is executed before the function being decorated starts, or after it returns. Or both. That “before and/or after” is what I mean by “around”.

The “group of” is also important. In theory you can create a decorator and apply it to a function, but that’s a waste of time. (You’ll know why once you start writing them.) When you create a decorator, you do so with the intention of applying it to at least two functions, probably many more.

Ok, the categories:

Category #1: One on One

The most useful decorators fall into this category.

The idea is that every time you call the decorated function, the bare function is called exactly once.

Remember, a decorator adds behavior around a function. That means it still calls the target function. Exactly once for each time the decorated function is called.

In fact, it might be a bit strange to consider NOT doing this. That’s a good instinct.

One of the reasons the one-to-one pattern is so useful is that it’s sensible. It results in code that is easy to reason about. But sometimes you need to deviate from that.

Category #2: Decoupled

You have two features to consider:

The bare function. The function to which you are applying a decorator.

And the decorated function. That’s the result you get, after applying the decorator. In a sense, this creates a new function.

In the “one-to-one” pattern, each time you call the decorated function, the bare function is called once. But you can decouple these two. You can write your code so that when you call the decorated function, it doesn’t call the basic function at all.

Or it calls the base function sometimes, but not others.

Or maybe call the basic function TWICE. Or more.

Is this a good idea? Can be. It is the basis of some very powerful patterns.

It also creates code that is difficult to reason about. Because it violates the “principle of least surprise.”

So there is a tradeoff.

Category #3: Pure Side Effect

This is interesting and strange.

But one of the most popular web frameworks in the world is fundamentally based on it.

In this type of decorator, there is no decorated function at all. Rather, the bare function is recorded as a side effect. So when the decorated function is called in your application… you are actually calling the bare function. But logging has other effects in other parts of the code.

Other categories

This is just one way to classify decorators. There are other useful ways to break it down.

How many can you recognize in the code you write or in the libraries you use every day?

About the author

Leave a Reply

Your email address will not be published. Required fields are marked *