In Post #138, we successfully built our first decorator by manually “wrapping” a function. We defined our original function, passed it to a decorator function, and then reassigned the returned wrapper back to the original function’s name.
This manual process, say_whee = my_decorator(say_whee)
, works perfectly and clearly reveals the mechanics of how a decorator works. However, it’s a bit repetitive and verbose. Because this pattern is so common and useful, Python provides a special, cleaner syntax to do the exact same thing.
In this post, we’ll learn about the @
symbol, which is Python’s “syntactic sugar” for applying decorators.
What is Syntactic Sugar?
Before we see the @
symbol, let’s understand the term “syntactic sugar.” It refers to syntax within a programming language that is designed to make things easier to read or to express. It doesn’t add any new functionality that wasn’t already possible; it’s simply a “sweeter,” cleaner way of writing something that you could also write in a more verbose way.
The @
Syntax
The @
symbol is Python’s syntactic sugar for applying a decorator to a function. You place the @
symbol followed by the decorator’s name on the line directly above the function you want to decorate.
Let’s look at a direct comparison. This code from our last post:
def say_whee():
print("Whee!")
# The manual reassignment step
say_whee = my_decorator(say_whee)
Is exactly equivalent to this code using the @
syntax:
@my_decorator
def say_whee():
print("Whee!")
The @my_decorator
line is just a beautiful shortcut for the reassignment say_whee = my_decorator(say_whee)
. Python does this for you automatically when it defines the say_whee
function.
A Complete Example
Let’s put it all together. First, we define our decorator, and then we use the @
syntax to apply it to a new function.
# 1. Define the decorator function
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
# 2. Apply the decorator to a new function using the @ syntax
@my_decorator
def say_hello():
print("Hello!")
# 3. Call the decorated function as usual
say_hello()
When we call say_hello()
, we are actually calling the wrapper
function that my_decorator
created and returned for us. The @my_decorator
line handled the reassignment behind the scenes. The output will be:
Something is happening before the function is called.
Hello!
Something is happening after the function is called.
What’s Next?
The @
syntax is the standard, Pythonic way to apply decorators. It’s clean, declarative, and clearly shows that a function is being modified or enhanced by another. While it’s just syntactic sugar for the manual pattern we learned first, it’s the syntax you will see and use in all modern Python code.
Now that we understand the theory and the syntax of decorators, it’s time to build some that are actually useful. In Post #140, we will create our first practical decorator: a @timer
that can be used to measure how long any function takes to run.
Author

Experienced Cloud & DevOps Engineer with hands-on experience in AWS, GCP, Terraform, Ansible, ELK, Docker, Git, GitLab, Python, PowerShell, Shell, and theoretical knowledge on Azure, Kubernetes & Jenkins. In my free time, I write blogs on ckdbtech.com