A function gives a name to a piece of behaviour so you can use it more than once, and so a reader can understand your program without reading every line of it.
def greet(name):
return f"Hello, {name}!"
print(greet("Ada"))
print(greet("Alan"))
message = greet("Grace")
print(message.upper())The anatomy: def, the name, the parameters in parentheses, a colon, then
an indented body. return hands a value back to whoever called it.
return, not print
This distinction is worth getting right on day one of functions.
def add_printing(a, b):
print(a + b) # shows the answer
def add_returning(a, b):
return a + b # hands the answer back
add_printing(2, 3)
result = add_printing(2, 3)
print("captured:", result) # None - nothing was returned
result = add_returning(2, 3)
print("captured:", result)
print(add_returning(2, 3) * 10) # usable in a bigger expressionA function that prints can only ever put text on a screen. A function that returns can be used in arithmetic, stored, tested, and printed if you want to. Return values; print at the edges of your program.
Every function returns something
A function with no return returns None. That is why print(add_printing(2, 3))
shows None — you are printing the absence of a result.
Several parameters, and defaults
A parameter can have a default value, which makes it optional at the call site.
def price_with_tax(amount, rate=0.2):
return round(amount * (1 + rate), 2)
print(price_with_tax(100)) # uses the default rate
print(price_with_tax(100, 0.05)) # overrides it
def greet(name, greeting="Hello", punctuation="!"):
return f"{greeting}, {name}{punctuation}"
print(greet("Ada"))
print(greet("Ada", "Welcome"))
print(greet("Ada", punctuation="?")) # skip the middle one by namingParameters with defaults must come after those without — Python has no way to guess otherwise.
Never default to a list
def add_item(item, basket=[]): looks reasonable and is a genuine trap: the
default list is created once, when the function is defined, and is shared by
every call that uses it. Use basket=None and create a fresh list inside.
def broken(item, basket=[]):
basket.append(item)
return basket
print(broken("apple"))
print(broken("pear")) # the apple is still there
def fixed(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basket
print(fixed("apple"))
print(fixed("pear"))Docstrings
A string on the first line of a function is its documentation. Tools read it,
help() prints it, and your future self will thank you.
def body_mass_index(weight_kg, height_m):
"""Return the BMI for a weight in kilograms and a height in metres.
The result is rounded to one decimal place.
"""
return round(weight_kg / height_m ** 2, 1)
print(body_mass_index(70, 1.75))
help(body_mass_index)Say what it returns and what the arguments mean. Do not restate the code.
Functions calling functions
Small functions that call each other is how programs stay readable as they grow.
def is_vowel(letter):
return letter.lower() in "aeiou"
def count_vowels(text):
count = 0
for letter in text:
if is_vowel(letter):
count += 1
return count
def describe(text):
vowels = count_vowels(text)
return f"{text!r} has {vowels} vowels out of {len(text)} characters"
print(describe("Programming"))
print(describe("rhythm"))Each function does one thing and is testable on its own. {text!r} in the
f-string means “show the repr” — the quoted, debugging-friendly form.
A tiny statistics toolkit
Write three functions — mean(values), largest(values) and
range_of(values) (the difference between the largest and smallest) — then use
them to report on the list. Each must return, not print.
readings = [18, 21, 25, 19, 30, 22]
# Your code hereShow one solution
readings = [18, 21, 25, 19, 30, 22]
def mean(values):
"""Return the arithmetic mean of a non-empty sequence of numbers."""
return sum(values) / len(values)
def largest(values):
"""Return the biggest value."""
return max(values)
def range_of(values):
"""Return the spread between the largest and smallest values."""
return max(values) - min(values)
print(f"Mean: {mean(readings):.1f}")
print(f"Largest: {largest(readings)}")
print(f"Range: {range_of(readings)}")largest is a thin wrapper over max and would not be worth writing in real
code — but naming the operations you care about is exactly how a domain
vocabulary starts.
What you learned
def name(parameters):defines a function; calling it runs the body.returnsends a value back; without it you getNone.- Return values rather than printing them, so callers can use the result.
- Default parameters make arguments optional — never default to a mutable value.
- A docstring documents what the function returns and what its arguments mean.