Full path: think, build, test, rebuild

Let's create a new function from scratch to see the whole process in one go.

Writing a function is never the goal. You write a function because you need to solve a problem repeatedly. So, it all starts with the problem, the task. Often, it comes from your client, a colleague or the manager. Let's say, you work for a retail company, and one day the boss says:

We need an app to calculate the billable amount for our invoices. The amount is based on three things: price of the item, quantity and a flat delivery fee. The formula is: amount = price × quantity + fee

Alright! It's a good fit for a Python function. To create a function, you have to make three decisions:

  1. How to name it.
  2. What comes in.
  3. What comes out.

1. How to name it

We've talked about technical aspects of naming a function previously. You know what symbols and words are and aren't allowed. But the semantic aspect of naming a function is far more important. For the interpreter it doesn't matter, it couldn't care less. But code is for people first, so you should give functions meaningful names. Names should reflect the purpose, the goal.

In our case the goal is to determine the billable amount. So, maybe, amount seems like a good option, but, alas, it's not great. It's a motionless noun. Functions perform actions, so it's better to give them verb names. get_amount or calc_amount (short for "calculate") is preferred. Let's pick the former and start the definition:

def get_amount(

2. What comes in

The amount is based on three things: price of the item, quantity and flat delivery fee. Therefore, three numbers should "come into" your function:

  1. Price
  2. Quantity
  3. Fee

These are going to be parameters, and we need names for them. Parameters are labels for values, so use nouns. Let's call them price, quantity and fee and continue building the definition:

def get_amount(price, quantity, fee):

3. What comes out

The goal of the function is to produce the answer: billable amount. While we haven't done anything to actually calculate it yet, we can just pretend that we did and write the final line of the function. It's like writing the ending of a story before writing the story.

The purpose of the function is to produce — return — the amount:

def get_amount(price, quantity, fee):
    
    return amount

Now we have a complete skeleton of the function. It's got a name, parameters and the final return statement. All what's left is to actually calculate the value of amount according to the formula given by the boss:

def get_amount(price, quantity, fee):
    amount = price * quantity + fee
    return amount

Calling it

Great! It's time to test if it works:

print(get_amount(100, 5, 21))   # →◼ 521
print(get_amount(121, 3, 19))   # →◼ 382
print(get_amount(227, 6, 4))    # →◼ 1366

Alternative implementations

You've seen that return can process any value or any expression, and we don't have to create a variable in order to return a value. Here's an alternative implementation of get_amount:

def get_amount(price, quantity, fee):
    return price * quantity + fee

Or we could go the other way and expand the implementation:

def get_amount(price, quantity, fee):
    cost = price * quantity
    amount = cost + fee
    return amount

Either way, the function behaves the same way, and for the caller it doesn't make a difference. There are always multiple ways to implement a function. The primary parts are always the same: the def keyword, the parentheses, indentation. But the actual code in the body is decided by the programmer. The amount of arguments depends on the task, though. Sometimes the task is vague, so the programmer has to decide on the amount of arguments. Sometimes the task is strict and the amount of arguments is a part of the task.


Without return?

There are a few cases when you may define functions without return:

  1. To print something onto the screen: put print inside the function and make the call.
  2. To use some magic stuff for adding new values to or removing values from a group of objects (we'll discuss this in the next course).
  3. The task at hand requires the function to return None.
  4. By mistake (you forgot to add return to function's body; remember — a function without return will return None)

If None is required to be returned, you may omit return completely, but we recommend explicitly writing return None.