Functions and methods
Thus far we've covered many primitive python operations, as well as called many functions, its past
due to talk about what a function is.
In simple terms: a function is a re-usable piece of code. It is one of the core building blocks of abstraction, allowing complex implementation details to be hidden away behind simple function call syntax.
Syntax.
the def keyword is used to define functions and methods. The minimum syntax is as follows:
def some_function():
... # inside `def some_function()`
# outside def some_function()
Like all control structures, the body of the function is denoted using indentation.
For a function to be re-usable, there are generally bits of data that need to be provided for the function to do anything useful.
Let's start with the geometric function that defines a line, y=mx+b.
"""
uci_bootcamp_2021/examples/functions.py
examples on how to use functions
"""
def y(x, slope, initial_offset):
"""
This is a docstring. it is a piece of living documentation that is attached to the function.
Note: different companies have different styles for docstrings, and this one doesn't fit any of them!
this is just a small example of how to write a function in python, using simple math for demonstration.
"""
return slope * x + initial_offset
A function is composed of three parts:
- the function signature contains the
def {{name}}({{parameters}}): - the function's docstring contains documentation of the function itself, such as what the function does and what is required to use it. Defaults to an empty string if not provided.
- the function body, which contains the code that is executed when the function is "called"
- A
functionmay accept zero-or-more parameters.
Calling a function
To use a function, we "call" it.
In earlier chapters we called several functions such as print, here we will clarify the syntax.
To call an object, you refer to it by name and add a pair of parenthesis.
For example,
# tests/test_examples/test_functions.py
# Comparing floats can only be done imprecisely
from math import isclose
# Bring the function defined in the example into scope.
from uci_bootcamp_2021.examples.functions import y
def test_y():
result = y(42, 0.5, 0)
# Note: cannot compare floats by equality; given floating point inaccuracy.
# Here we assert that 21.0 is close to the output of the function given known inputs.
assert isclose(result, 21.0)
When calling a function, you can also specify which parameters the arguments fill. this is known
as keyword arguments
For example, using the same function as above:
# tests/test_examples/test_functions.py
...
def test_y_kwargs():
result = y(x=42, initial_offset=0, slope=0.5)
# Note: cannot compare floats by equality; given floating point inaccuracy.
# Here we assert that 21.0 is close to the output of the function given known inputs.
assert isclose(result, 21.0)
keyword arguments can be presented out of order, but MUST only come after all positional arguments.
- the caller can also only present one argument for a given parameter
Applying type annotations to function definitions
Type annotations can also be applied to function definitions, to improve readability and documentation.
For the above function, it can also be written as
def y(x: float, slope: float, initial_offset: float = 0) -> float:
"""Same function as above, but this time with type annotations!"""
return slope * x + initial_offset
-> floatindicates the value returned by the function is an instance offloat.
Something less contrived
Math functions aside, show me a function that isn't better off copy/pasted into the callsite!
Sure. Remember that get_valid_input while loop presented in the with for loop
chapter? Here is the same piece of code in function form!
# uci_bootcamp_2021/examples/while_loops.py
def get_valid_input(
prompt: str, valid_minimum: int = 0, valid_maximum: int = 100
) -> int:
# initialize valid to False.
valid = False
result = -1
# loop while the user hasn't given us a suitable value.
while not valid:
# get the user's untrusted input.
untrusted_input = input(prompt)
# check if the input is numeric.
if untrusted_input.isnumeric():
# if its numeric, we can safely interpret it as an integer
result = int(untrusted_input)
# then we can check the bounds
valid = valid_minimum <= result <= valid_maximum
if not valid:
print("invalid input, please try again!")
return result