Remember
A decorator takes a callable and supplies its replacement. A wrapper can do work before or after delegating to the original function.
A decorator takes a callable and supplies its replacement. A wrapper can do work before or after delegating to the original function.
Follow the example, change its sample, and compare what happens.
Explain the result and test your understanding.
Focus on how the names, objects, and calculations change. We’ll inspect the printed output in the next section.
Follow the code and watch its state change. Guided simulation.
def announce(function):
def wrapper():
print("before")
function()
print("after")
return wrapper
@announce
def greet():
print('hello')
greet()Top-level scope
function announceObject 3 · functionfunction wrapperA decorator takes a callable and supplies its replacement. A wrapper can do work before or after delegating to the original function.
(no output)
What do you expect this program to print?
EXAMPLE INSTRUCTIONS
def announce(function):
def wrapper():
print("before")
function()
print("after")
return wrapper
@announce
def greet():
print('hello')
greet()
|
v
DISPLAYED RESULT
before
hello
after
FINAL BINDINGS
announce --> object 1 (function)
greet --> object 3 (function)
object 1: function announce
object 3: function wrapperChoose a different sample input, then follow every step through to the output. Before revealing it, predict what the program will display.
Choose a sample and follow its execution. Guided simulation.
def announce(function):
def wrapper():
print("before")
function()
print("after")
return wrapper
@announce
def greet():
print('hello')
greet()Top-level scope
function announceObject 3 · functionfunction wrapperThe example has finished. Compare the final output with the values and references above.
before hello after
What do you expect this program to print?
def announce(function):
def wrapper():
print("before")
function()
print("after")
return wrapper
@announce
def greet():
print('hello')
greet()Decorators in Python allows you to modify the behavior of functions or classes. If you're coming from a beginner’s perspective, think of a decorator as a "wrapper" that adds extra features to an existing function without changing its core code.
Imagine you want to log the execution time of various functions or check user authentication before executing a task. You could repeat that logic inside every function... or, you could just use a decorator. It keeps your code clean, modular, and reusable.
Before diving into decorators, it’s important to know that in Python, functions are first-class objects. This means:
def greet():
return "Hello!"
say_hello = greet
print(say_hello())
Hello!
A decorator is simply a function that takes another function as input and returns a new function with added behavior.
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
def say_hi():
print("Hi!")
decorated = my_decorator(say_hi)
decorated()
Something is happening before the function is called.
Hi!
Something is happening after the function is called.
Python provides a cleaner way to apply decorators using the @ symbol.
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
@my_decorator
def say_hi():
print("Hi!")
say_hi()
Something is happening before the function is called.
Hi!
Something is happening after the function is called.
If your target function has parameters, your wrapper needs to accept them too.
def log_args(func):
def wrapper(*args, **kwargs):
print(f"Arguments passed: {args}, {kwargs}")
return func(*args, **kwargs)
return wrapper
@log_args
def add(x, y):
return x + y
print(add(5, 7))
Arguments passed: (5, 7), {}
12
Always remember to return the result of the original function if it produces one.
When you use decorators, the metadata of the original function (like its name or docstring) can get lost. To preserve it, use functools.wraps.
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper
@my_decorator
def greet(name):
"Greets a user by name"
print(f"Hello, {name}")
print(greet.__name__)
print(greet.__doc__)
Before function call
Hello, Alice
After function call
greet
Greets a user by name
Python includes several built-in decorators:
@staticmethod@classmethod@propertyclass MyClass:
@staticmethod
def static_method():
print("This is a static method.")
@classmethod
def class_method(cls):
print(f"This is a class method from {cls.__name__}")
@property
def name(self):
return "TutorialKart"
obj = MyClass()
obj.static_method()
obj.class_method()
print(obj.name)
This is a static method.
This is a class method from MyClass
TutorialKart
functools.wraps when writing decorators.To ensure your decorator works as expected:
__name__ and __doc__.Decorators are one of Python’s elegant ways to inject functionality without altering existing code.
Connect what you noticed in the example with the idea below.
A decorator takes a callable and supplies its replacement. A wrapper can do work before or after delegating to the original function.
A decorator can only change a function’s name.
This is the misconception to avoid.Reaching the recap and completing the quizzes are separate milestones.