Annotations
Python’s decorators vs Java’s annotations, same thing?¶
If you know Java and happened to work with Python, or the other way, you could see some @ symbols with names above the function in both languages. If you thought they’re similar, congratulations, you’ll be surprised by their behavior in the most unsuitable moment 😏
What the hell are those @ signs above function anyway?¶
In Java, they called annotations. Annotation is, in simple words, metadata. You can pass data into the annotation, and, with some work, read this metadata. Here’s a small example of annotation usage from Spring framework(Class was omitted for simplicity):
1 2 3 4 5 6 |
|
You can see that ‘@RequestMapping’ used to specify that we want to execute this function when the client calls the GET method /hello
endpoint.
In Python, oppositely, they called decorators. Here’s an example from Flask application, as in Java’s example, our decorator will make so our function will serve /hello
endpoint with GET method:
1 2 3 4 5 |
|
A decorator is a design pattern that allows a user to add new functionality to an existing object without modifying its structure.
Ah okay, problem solved. So Java’s annotation just adds metadata, and Python’s decorators modify usage of the function.
Thank you for reading this… wait for a second! So if Java’s annotation just adds metadata, how our method became endpoint server as in Python? In other words, how they both do the same thing?? Well, let’s start by implementing our own decorator in Python.
Decorators in Python, how they work?¶
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Key takeaways from code above:
- Decorator is just a function that returns another function
- If applied with @ sign above the function, it’ll change it’s behavior as specified in
wrapper()
function - They can change input or output of a wrapped function, or even don’t call it at all.
What is going on in the code above? Firstly we create simple function as_html(func)
. In this function we create another function called wrapper()
, because in Python function is just another object that you can use. Inside wrapper()
we will call this func object from as_html(func)
, which, as you can think, another function. And so basically we return our wrapper()
function. Take a cup of coffee and try to structure what’s going on in your head.
So now, we annotate this function with @ above the say_hello()
, and, as a result, you can see that when we call say_hello()
, we get different result than ‘Hello’. We get ‘Hello’ because our as_html(func)
wrapped our say_hello()
function. So func is the say_hello()
function itself. Maybe if I put it like as_html(say_hello)
makes it clearer to you. Technically, you can re-write the previous example like this, and it still works the same way:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Cached decorator for functions¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
This is (kinda) more practical example, imagine that you have some function that should execute only once, and all other times return previous result. You can, of course, implement it within the function itself, but what if you’ll have 10 of them? 100? That’s a decorator’s work.
So, in wrapper(*args, **kwargs)
we check if we already contain this function name in the global cached_items dictionary. If not, we execute the function, if yes, we only return the value of dictionary for a key with the function name. After it, we wrap intensive_task() with our @cached decorator and execute function two times with benchmarking. Here’s what I get on my pc:
1 2 3 |
|
You can clearly see that second time execution time took under a 7E-06 second even though in our intensive_task() we execute sleep command. So the body of function was executed only once, as we can see by logic in wrapper()
Now you can call yourself a junior expert in decorators in Python. So what about Java?
Annotations in Java, are they decorators?¶
When I first encountered annotations in Java, they were really astonishing to me. So later when I encountered anything with @ sign in other languages I said: “oh, that’s an annotation, I know that!”. Only now I realized how different those two things are.
In Java, instead of writing the logic for “decorator” in annotation itself, this responsibility falls onto executor. So, technically speaking, annotations in Java doesn’t contain any logic at all*, only some data that can be put as a variable. So how it happens that we can do the same logic in Java as in Python? Fairly easy, we just inverse our code a little bit.(be ready for large piece of code due to Java ;)
There can be annotation pre-processor like Lombok, but main idea is still the same. Unless defined(even if in pre-processing), annotations won’t do any changes to behavior. More discussion on it in responses of this post.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
So, what happened here? Here are a few points:
- The annotation doesn’t do anything at all. As the name says, it only annotates.
- Instead of wrapping functions, we check if it contains some annotation and execute it differently.
- We should have some “executor” object which will hold all the logic that could be in python’s decorator.
So, first thing first, we created @Cached
annotation, which is just empty, nothing special here except ‘@Retention(RetentionPolicy.RUNTIME)’. This thing says that we can access this annotation when the program is running. Next, we have our SomeObject, because in Java functions are not object, and we can’t pass them into other functions. In this object, we have intensiveTask()
, which will sleep when executed. The next thing that we made is SomeObjectExecutor, which takes our SomeObject
and executes its function via Object Reflection.
To combine it all, we use main() method, where we create SomeObjectExecutor
and SomeObject
and then pass SomeObject
two times with benchmarking, here’s my output:
1 2 |
|
As expected, the First time we execute an object it takes 1 second, next time — almost 0, because the object was cached.
What about other retention policy?¶
If you work with Java for a while, you can know such thing as aspect-oriented programming(AOP), AspectJ and similar tools. I even made an article about it a few years ago. So AspectJ kinda allows you to convert your annotations into decorators. But there’s still a small gap between the AOP and decorators, and you need to apply more efforts in Java with AOP then in Python. I may cover this topic of how pre-compilers work in the next article.
Conclusion¶
If you read through the article up to this point, you can see that both approaches have their benefits and disadvantages. Because in Python functions are first-class citizens, it’s really easy to juggle with them and pass one function into another, etc. Java’s approach, on the other hand, gives you more control over what’s going on.