avatarLiu Zuo Lin

Summarize

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 as sayhello
  • wrapper_function becomes:
def wrapper_function(name):
  return sayhello(name) + '!'
  • this new wrapper_function is then returned by add_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 or function3, 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:

  1. Clap 50 times for this story
  2. Leave a comment telling me what you think
  3. Highlight the parts in this story that resonate with you
  4. 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/

Python
Decorators
Programming
Python Programming
Recommended from ReadMedium