Functions in Python: def, Arguments and return

Python for Beginners: Learn to Code with Real Projects · Functions, Text and Files · 6 min read · Updated

Calling a function

  1. Your code calls the function and passes arguments.
  2. The values arrive in the parameters, which are local names inside the function.
  3. The body runs with those values and does the calculation.
  4. return sends the result back to the place where the function was called.

A function is a named block of code that you can run whenever you need it. You have already used functions that other people wrote: print, len, input and int. Writing your own lets you give a name to a piece of work, use it many times without copying the code, and change it in one place. Programs made of small, clear functions are much easier to read, test and fix.

Defining and calling a function

You make a function with the word def, a name, brackets with parameters, a colon, and an indented body. return sends a value back to the code that called the function. The values you pass when you call it are called arguments. Run this program and compare each line with its output.

def add_tax(amount, rate=0.18):
    """Return the amount with tax added."""
    return round(amount * (1 + rate), 2)

def min_max(numbers):
    return min(numbers), max(numbers)

def describe(name, *, city="unknown", age=None):
    parts = [name, f"from {city}"]
    if age is not None:
        parts.append(f"age {age}")
    return ", ".join(parts)

print(add_tax(100))
print(add_tax(100, rate=0.05))
low, high = min_max([4, 9, 2, 7])
print(low, high)
print(describe("Asha", city="Pune", age=29))
print(describe("Ravi"))

# A function without return gives None
def say_hi():
    print("hi")
print(say_hi())

# Variables inside a function stay inside
def counter():
    count = 0
    count += 1
    return count
print(counter(), counter())

# A common trap: a list as a default value
def bad_add(item, items=[]):
    items.append(item)
    return items
print(bad_add("a"))
print(bad_add("b"))

def good_add(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items
print(good_add("a"))
print(good_add("b"))
118.0
105.0
2 9
Asha, from Pune, age 29
Ravi, from unknown
hi
None
1 1
['a']
['a', 'b']
['a']
['b']

Understanding the example

  • add_tax(amount, rate=0.18) has a default value for rate. If you do not give it, 0.18 is used. add_tax(100, rate=0.05) passes it by name, which is called a keyword argument and makes calls easy to read. The result prints as 118.0 and not 118, because round of a float returns a float.
  • The text in triple quotes right after the first line is a docstring. It documents what the function does, and tools and editors show it to you.
  • min_max returns two values. Python packs them into a tuple, and low, high = min_max(...) unpacks them. This is how a function can give several results.
  • In describe, the * in the parameter list means that everything after it must be passed by name. That avoids mixing up arguments. age=None means optional, and the code checks is not None.
  • say_hi() has no return, so the call gives None, Python's value for nothing. Printing it shows None.

Scope: where a variable lives

A variable created inside a function exists only inside that function. It is local. In the example, counter() creates count each time it is called, so it returns 1 every time. Variables outside any function are global. A function can read a global variable, but if it assigns to a name, Python creates a new local variable. Beginners are tempted to use global variables to share data. A better habit is to pass values in as arguments and get results out with return.

The mutable default trap

Look at the last part of the output. bad_add("a") gives ['a'], but the second call gives ['a', 'b'], although we never passed the first list. Python makes the default value once, when the function is defined, and a list is changed in place, so every call shares the same list. The fix, shown in good_add, is to use None as the default and create a fresh list inside the function. Remember this rule: never use a list or dictionary as a default value.

Writing good functions

  • One job each. If you need the word *and* to describe it, think about splitting it.
  • Clear names with a verb: calculate_total, load_orders.
  • Return values instead of printing inside the function when you can. A function that returns a result can be tested and reused. A function that only prints is hard to reuse.
  • Keep them short. If a function does not fit on one screen, it is probably doing too much.
  • Check the input at the start when wrong values would cause a confusing failure later.

Functions that call functions

Small functions can use each other. For example, a final_price(amount) function can call add_tax, and a print_receipt(items) function can call final_price for each item. This is how larger programs are built: small steps joined together. Read the program from the bottom, where the main code is, to the top, where the helpers are.

Lambda and other short forms

Python has a short way to write a tiny unnamed function, called a lambda: lambda x: x * 2. You will see it as a sorting key, for example sorted(items, key=lambda item: item[1]). For anything longer than one expression, use a normal def. It is easier to read.

Your task

  1. Write total_with_tax(prices, rate=0.18) that takes a list of prices and returns the total with tax, rounded to two decimals.
  2. Write stats(numbers) that returns the smallest, the largest and the average. Call it and unpack the three results.
  3. Write is_valid_age(age) that returns True for ages from 0 to 120 and False otherwise. Test the edges 0, 120 and 121.
  4. Copy the bad_add example, run it twice and then fix it as shown.

How to check it worked

  • total_with_tax([100, 200]) returns 354.0.
  • is_valid_age(121) returns False and is_valid_age(0) returns True.
  • You can explain why the mutable default argument gives surprising results.

Frequently asked questions

What is the difference between a parameter and an argument?

A parameter is the name in the function definition. An argument is the actual value you pass when you call the function.

Should a function print or return?

Prefer return. A function that returns a value can be reused and tested. Print the result in the code that calls it.