Decorator in Python
Published in
3 min readMay 5, 2024
A good syntax sugar in python to improve the coding efficiency.
A good source for practicing Python decorator — ReaPython
Decorator
def 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 do_twice(func):
def wrapper_do_twice():
func()
func()
return wrapper_do_twice
@do_twice
def say_whee():
print("Whee!")
print(say_whee())
But if we change the decorator
def 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 do_twice(func):
def wrapper_do_twice():
func()
func()
return wrapper_do_twice
@decorator
def say_whee():
print("Whee!")
print(say_whee())
With Arguments
def greet(func):
def wrapper_do_twice(*args, **kwargs):
func(*args, **kwargs)
func(*args, **kwargs)
return wrapper_do_twice
@greet
def say_whee(name):
print(f"Hola {name}")
print(say_whee("Python"))
Return values from decorated functions
def do_twice(func):
def wrapper_do_twice(*args, **kwargs):
func(*args, **kwargs)
return func(*args, **kwargs)
return wrapper_do_twice
@do_twice
def return_greeting(name):
print("Creating greeting...")
return f"Hey {name}!"
print(return_greeting("Hobbit"))
This does the same.
def do_twice(func):
def wrapper_do_twice(*args, **kwargs):
func(*args, **kwargs)
return func(*args, **kwargs)
return wrapper_do_twice
@do_twice
def return_greeting(name):
print("Creating greeting...")
return f"Hey {name}!"
hob = return_greeting("Hobbit")
print(hob)
Timing Function
import functools
import time
def decorator(func):
@functools.wraps(func)
def wrapper_decorator(*args, **kwargs):
# Do something before
value = func(*args, **kwargs)
# Do something after
return value
return wrapper_decorator
def timer(func):
# Print the runtime of the decorated function
@functools.wraps(func)
def wrapper_timer(*args, **kwargs):
start_time = time.perf_counter()
value = func(*args, **kwargs)
end_time = time.perf_counter()
run_time = end_time - start_time
print(f"Finished {func.__name__}() in {run_time:.4f} secs")
return value
return wrapper_timer
@timer
def waste_some_time(num_times):
for _ in range(num_times):
sum([number**2 for number in range(10_000)])
waste_some_time(1)
# Finished waste_some_time() in 0.0010 secs
waste_some_time(999)
# Finished waste_some_time() in 0.3260 secs
Debug
import functools
def debug(func):
# """Print the function signature and return value"""
@functools.wraps(func)
def wrapper_debug(*args, **kwargs):
args_repr = [repr(a) for a in args]
kwargs_repr = [f"{k}={repr(v)}" for k, v in kwargs.items()]
signature = ", ".join(args_repr + kwargs_repr)
print(f"Calling {func.__name__}({signature})")
value = func(*args, **kwargs)
print(f"{func.__name__}() returned {repr(value)}")
return value
return wrapper_debug
@debug
def make_greeting(name, age=None):
if age is None:
return f"Howdy {name}!"
else:
return f"Whoa {name}! {age} already, you're growing up!"
print(make_greeting("Benjamin"))
print(make_greeting("Juan", age=114))
print(make_greeting(name="Maria", age=116))