Three Categories of Python Decorators

This article won’t make sense to all Python developers.

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

Which is why only 1% of all Python developers will ever learn to write them, by my reckoning.

That’s a shame. The most important 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 to develop massively powerful libraries yourself.

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

What is a decorator? It’s a way to add behavior around a group of functions or methods.

Every part of that definition is important. Read this whole article 10 times, if you have to, until you understand it.

You apply a decorator to a function (or method). Actually, a group of them. The result is called a “decorated function”. (Or method. Just add “or method” every time I mention a function.)

“Behavior” means “lines of code”. Code that’s 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 important too. You can theoretically create a decorator and apply it to one 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 to apply it to at least two functions – probably many more.

Okay, the categories:

Category #1: One-To-One

Most useful decorators fall in this category.

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

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

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

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

Category #2: Decoupled

You have two functions to consider:

The bare function. The function you’re applying a decorator to.

And the decorated function. That’s the result you get, after the decorator is applied. 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 bare function at all.

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

Or maybe it calls the bare function TWICE. Or more.

Is this a good idea? It can be. It’s the basis of some very powerful patterns.

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

So there’s a trade-off.

Category #3: Pure Side Effect

This one is interesting, and weird.

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

In this kind of decorator, there is no decorated function at all. Rather, the bare function is registered as a side effect. So when the decorated function is called in your application… you’re actually calling the bare function. But the registration has other effects elsewhere in 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?



Source by Aaron Maxwell

Popular Posts

Leave a Reply