
You’ve seen them before. In Flask, you use @app.route("/"). In Django, you might see @login_required. These are Decorators, and understanding Python decorators can significantly enhance your coding skills.
A decorator is a way to “wrap” extra functionality around a function without changing the function’s actual code.
The “Manual” Way (Without @)
Imagine you want to log every time a function is called.
def my_function():
print("Doing the real work...")
def log_wrapper(func):
def wrapper():
print("LOG: Function is about to run...")
func()
print("LOG: Function finished.")
return wrapper
# Wrap it manually
logged_function = log_wrapper(my_function)
logged_function()The “Decorator” Way (With @)
Python gives us the @ syntactic sugar to do this cleanly.
def log_decorator(func):
def wrapper():
print(f"LOG: Running {func.__name__}...")
func()
print("LOG: Finished.")
return wrapper
@log_decorator
def say_hello():
print("Hello, world!")
@log_decorator
def do_math():
print(f"2 + 2 = {2 + 2}")
# Now just call them normally!
say_hello()
do_math()Output:
LOG: Running say_hello... Hello, world! LOG: Finished. LOG: Running do_math... 2 + 2 = 4 LOG: Finished.
We added logging to two totally different functions without changing a single line of their internal code!





