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 forrate. 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 as118.0and not118, becauseroundof 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_maxreturns two values. Python packs them into a tuple, andlow, 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=Nonemeans optional, and the code checksis not None. say_hi()has noreturn, so the call givesNone, Python's value for nothing. Printing it showsNone.
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
- Write
total_with_tax(prices, rate=0.18)that takes a list of prices and returns the total with tax, rounded to two decimals. - Write
stats(numbers)that returns the smallest, the largest and the average. Call it and unpack the three results. - Write
is_valid_age(age)that returnsTruefor ages from 0 to 120 andFalseotherwise. Test the edges 0, 120 and 121. - Copy the
bad_addexample, run it twice and then fix it as shown.
How to check it worked
total_with_tax([100, 200])returns354.0.is_valid_age(121)returnsFalseandis_valid_age(0)returnsTrue.- You can explain why the mutable default argument gives surprising results.