Changing A Function Without Changing The Function in Python
Let’s say you have a Python function.
def sayhello(name):
return f'hello {name}'
And now you want to slightly change the function. Let’s say you wish modify the function, and add an exclamation mark after its return value. So instead of hello tom
, it returns hello tom!
.
Easy! I simply need to tweak the return value right?
def sayhello(name):
return f'hello {name}!'
Yep that could work. But what if I told you that you could do this without touching the code for the sayhello
function?
Say Hello To Decorators
Decorators are higher order functions. They are functions themselves, but they take in a function, and also return a function.
More specifically, they take in a function you want to change the behaviour of, do some stuff to that function, and return a new function with that updated behaviour.
# this is the decorator
def add_exclamation_mark(your_function):
def wrapper_function(*args, **kwargs):
return your_function(*args, **kwargs) + '!'
return wrapper_function
# decorating your function
@add_exclamation_mark
def sayhello(name):
return f'hello {name}'
print(sayhello('tom')) # hello tom!
Let’s break this down step by step
1) The @ Syntax
# decorating your function
@add_exclamation_mark
def sayhello(name):
return f'hello {name}'
print(sayhello('tom')) # hello tom!
^ this code block above is the exact same as:
def sayhello(name):
return f'hello {name}'
# decorating your function (this is what the @ syntax does)
sayhello = add_exclamation_mark(sayhello)
print(sayhello('tom')) # hello tom!
2) What happens in add_exclamation_mark
# this is the decorator
def add_exclamation_mark(your_function):
def wrapper_function(*args, **kwargs):
return your_function(*args, **kwargs) + '!'
return wrapper_function
sayhello = add_exclamation_mark(sayhello)
add_exclamation
returns wrapper_function
, so sayhello
is actually being reassigned to wrapper_function
.
3) What happens in wrapper_function
wrapper_function
takes in the same stuff as your function thanks to *args
and **kwargs
. Because your_function
(which is sayhello
) takes in name
, wrapper_function
also takes in just name
.
It’s kinda the same as defining:
def wrapper_function(name):
return sayhello(name) + '!'
which adds an additional exclamation mark after the return value like we want it to.
A quick summary
# this is the decorator
def add_exclamation_mark(your_function):
def wrapper_function(*args, **kwargs):
return your_function(*args, **kwargs) + '!'
return wrapper_function
# decorating your function
@add_exclamation_mark
def sayhello(name):
return f'hello {name}'
print(sayhello('tom')) # hello tom!
your_function = sayhello
wrapper_function
takes in the same stuff assayhello
wrapper_function
becomes:
def wrapper_function(name):
return sayhello(name) + '!'
- this new
wrapper_function
is then returned byadd_exclamation_mark
sayhello = wrapper_function
- if we call
sayhello
now, its return value has an additional!
character.
And this is how we can change a function without changing the function!
A more realistic application of this
Let’s say we have multiple functions that could cause errors in our Python script. And we want to modify our functions to:
- not crash our entire script
- send us an email of the error message if an error occurs
from elsewhere import send_email
def my_decorator(your_function):
def wrapper_function(*args, **kwargs):
try:
return your_function(*args, **kwargs)
except Exception as e:
send_email(e)
return wrapper_function
@my_decorator
def function1(stuff):
# do stuff
@my_decorator
def function2(stuff):
# do stuff
@my_decorator
def function3(stuff):
# do stuff
- when we run either
function1
,function2
orfunction3
, errors won’t simply crash our script anymore - when an error happens for all 3 functions, an email will be sent
- we don’t have to write this error handling code individually in all 3 functions
Conclusion
Hopefully this was helpful and clear!
Some Final words
If this story was helpful and you wish to show a little support, you could:
- Clap 50 times for this story
- Leave a comment telling me what you think
- Highlight the parts in this story that resonate with you
- Sign up for a Medium membership using my link ($5/month to read unlimited Medium stories)
These actions really really help me out, and are much appreciated!
My Ebooks: https://zlliu.co/ebooks
My LinkedIn: https://www.linkedin.com/in/zlliu/