Lecture 3: Functions and Parameters¶
Welcome back! Last time we learned about the basic building blocks of programming: variables, types, and expressions. Today we're going to learn how to organize our code into reusable pieces called functions. By the end of this lecture, you will be able to:
- define your own functions
- understand parameters and arguments
- call functions from within other functions
- write documentation for your functions
And you will be equipped to start looking at Task 2 of HW1!
What Are Functions?¶
So far, we've been using some built-in functions like print(), type(), int(), and float(). But what exactly is a function?
A function is a named sequence of statements that performs a specific task. Think of it like a recipe in cooking - once you write down the recipe (define the function), you can use it over and over again without rewriting all the steps.
A metaphor in knitting is that imagine you have a pattern for knitting a specific cable stitch. Instead of writing out "cross 4 stitches to the left, then knit 4" every single time, you could just say "make one cable", and define what you mean by a "cable". This is exactly how functions work in programming!
Defining Your First Function¶
Let's start by defining a simple function. A function definition specifies the name of the function and the statements that run when it's called.
def print_gauge():
print("Stockinette gauge: 18 stitches x 24 rows per 4 inches")
print("Remember to check your gauge before starting!")
Let's break down what's happening here:
defis a keyword that tells Python we're defining a functionprint_gaugeis the name of our function (following the same naming rules as variables)- The empty
()parentheses indicate this function doesn't take any arguments (yet!) - The
:colon marks the end of the function definition (the first line) - The indented lines below are the function body (the indentation is part of the syntax)
By convention, the indentation is 4 spaces. Some people prefer 1 tab, while some others prefer 2 spaces. You can really choose any that's reasonable or to your taste as long as you are consistent with the amount of indentation.
This is not just about being orderly and easy for the eye to read through; the identation is in fact crucial for the Python Interpreter to interpret the code. Indented blocks of code are considered to be "local", or contained by what comes immediately before it. So the function body is indented and considered belonging to the function definition.
def print_gauge():
# inconsistent indentation: Python doesn't know how to interpret
print("Stockinette gauge: 18 stitches x 24 rows per 4 inches")
print("Remember to check your gauge before starting!")
Cell In[4], line 4 print("Remember to check your gauge before starting!") ^ IndentationError: unexpected indent
Calling a Function¶
When you run the cell that defines print_gauge(), Python creates a function object, but nothing is printed yet. The function is defined but not called, just like knowing the function definition of some function f(x) is not going to compute its value until we evaluate this function with some input value x.
To actually run the code inside a function, we need to call it. We do this by writing the function name followed by parentheses.
print_gauge()
Stockinette gauge: 18 stitches x 24 rows per 4 inches Remember to check your gauge before starting!
Now the statements in the body execute and we see the output!
Notice that we can call this function as many times as we want:
print_gauge()
print() # without any string input print() prints a newline character '\n'
print_gauge()
Stockinette gauge: 18 stitches x 24 rows per 4 inches Remember to check your gauge before starting! Stockinette gauge: 18 stitches x 24 rows per 4 inches Remember to check your gauge before starting!
This is the power of functions: write once, use many times!
In general, you want to reuse code as often as you can. In software engineering, there is a saying of "do not reinvent the wheel". And of course, this does not always apply, for example, if you do not have access to some proprietary software, or if the academic paper you are trying to replicate did not release their code, then you do need to reinvent the wheel. This principle more often applies to things like standard libraries for a language. For example, we have used round() and some functions from the math library. You do not want to re-implement these functions because it is unlikely for an individual to produce a more optimized and thorough implementation than an organization of language developers.
Functions with Parameters¶
The print_gauge() function always prints the same message. What if we want to make it more flexible? That's where parameters come in.
A parameter is a variable that allows us to pass information into a function. Think of it like a blank in a knitting pattern that you fill in with your specific measurements.
def print_stitch_count(num_stitches):
print(f"Cast on {num_stitches} stitches.")
print("Work in stockinette stitch until piece measures desired length.")
Here, num_stitches is a parameter - it's a variable that will hold whatever value we pass in when we call the function.
When we call the function, the value we provide is called an argument:
print_stitch_count(100)
Cast on 100 stitches. Work in stockinette stitch until piece measures desired length.
When this function runs:
- Python assigns the argument value
100to the parameternum_stitches - The function body executes with
num_stitches = 100 - The f-string inserts the value into the message
We can call the same function with different arguments:
print_stitch_count(80)
print()
print_stitch_count(120)
Cast on 80 stitches. Work in stockinette stitch until piece measures desired length. Cast on 120 stitches. Work in stockinette stitch until piece measures desired length.
Using Variables as Arguments¶
We can also pass variables as arguments:
beanie_stitches = 96
print_stitch_count(beanie_stitches)
Cast on 96 stitches. Work in stockinette stitch until piece measures desired length.
In this case, Python takes the value stored in beanie_stitches and assigns it to the parameter num_stitches inside the function.
The variable name outside the function (beanie_stitches) does not have to match the parameter name inside the function (num_stitches)! What matters is the value that gets passed.
Functions with Multiple Parameters¶
Functions can have more than one parameter. We separate them with commas:
def calculate_fabric_size(width_stitches, length_rows, stitches_per_inch, rows_per_inch):
width_inches = width_stitches / stitches_per_inch
length_inches = length_rows / rows_per_inch
print(f"Your fabric will be approximately {width_inches:.1f} inches wide"
+f" and {length_inches:.1f} inches long.")
When calling a function with multiple parameters, we provide the arguments in the same order:
# Calculate size for a scarf: 40 stitches wide, 200 rows long, with gauge 5 st/inch, 6 rows/inch
calculate_fabric_size(40, 200, 5, 6)
Your fabric will be approximately 8.0 inches wide and 33.3 inches long.
# Calculate size for a dishcloth: 50 stitches wide, 60 rows long, with gauge 4 st/inch, 5 rows/inch
calculate_fabric_size(50, 60, 4, 5)
Your fabric will be approximately 12.5 inches wide and 12.0 inches long.
Calling Functions from Other Functions¶
One powerful feature of functions is that you can call one function from inside another function. This allows you to build complex programs from simpler building blocks.
Here's an example with a knitting pattern that has repeating elements:
def knit_row(stitch_type, num_stitches):
"""Print instructions for knitting a single row."""
print(f"{stitch_type} across {num_stitches} stitches")
def knit_ribbing(total_stitches, num_rows):
"""Print instructions for knitting 1x1 ribbing."""
print(f"Work {num_rows} rows of 1x1 ribbing:")
for i in range(num_rows):
print(f"Row {i+1}: ", end="")
knit_row("K1, P1", total_stitches)
print()
Notice:
knit_ribbing()callsknit_row()inside its body- This is perfectly fine! Once a function is defined, you can use it anywhere, including inside other functions
- There are some more advanced structures here, like the
forloop,range, andend=""in theprintstatement; we'll learn more about these later
Let's try it:
knit_ribbing(80, 4)
Work 4 rows of 1x1 ribbing: Row 1: K1, P1 across 80 stitches Row 2: K1, P1 across 80 stitches Row 3: K1, P1 across 80 stitches Row 4: K1, P1 across 80 stitches
Documenting Your Functions¶
If you are wondering about the triple quotes """...""", these are called docstrings (documentation strings). They describe what the function does.
Good docstrings are essential for writing maintainable code. They help you remember what your function does when you come back to it later, and they help others understand your code.
Here's the recommended format for docstrings in this course:
def calculate_yarn_needed(num_stitches, yards_per_stitch):
"""
Calculates the total yards of yarn needed for a project.
Parameters:
- num_stitches: total number of stitches in the project
- yards_per_stitch: yards of yarn needed per stitch (typically 0.1)
The function prints the total yardage needed.
"""
total_yards = num_stitches * yards_per_stitch
print(f"You will need approximately {total_yards:.1f} yards of yarn.")
The docstring should include:
- What the function does (first line)
- What parameters it takes and what they mean
- What the function outputs or returns (we'll learn about return values next lecture!)
You can view a function's docstring using the help() function:
help(calculate_yarn_needed)
Help on function calculate_yarn_needed in module __main__:
calculate_yarn_needed(num_stitches, yards_per_stitch)
Calculates the total yards of yarn needed for a project.
Parameters:
- num_stitches: total number of stitches in the project
- yards_per_stitch: yards of yarn needed per stitch (typically 0.1)
The function prints the total yardage needed.
Poll Time!¶
Let's take a pause and try to review what we just talked about. Try going over all the code pieces that we have seen today. They are slightly more complex than last time's examples. What kind of common styles do you notice about the code? For example, the convention of indenting with 4 spaces. Do you notice any other conventions about spacing, whitespace, naming, lengths of lines? Enter your findings in the poll, and discuss with your neighbors!
This course also aims to help you develop an understanding of the Python coding style; this not only helps you parse code more easily but also helps you write code that others can more easily read. Remember to refer to the style guide.
print_gauge # This just shows the function object
<function __main__.print_gauge()>
type(print_gauge) # You check the type
function
print_gauge() # This actually calls the function
Stockinette gauge: 18 stitches x 24 rows per 4 inches Remember to check your gauge before starting!
Mistake 2: Wrong Number of Arguments¶
If a function expects parameters, you must provide the right number of arguments as listed in the function definition:
# This will cause an error: function expects 1 argument but we gave 0
print_stitch_count()
# This works correctly by supplying exactly 1 argument
# print_stitch_count(100)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[25], line 2 1 # This will cause an error: function expects 1 argument but we gave 0 ----> 2 print_stitch_count() 4 # This works 5 # print_stitch_count(100) TypeError: print_stitch_count() missing 1 required positional argument: 'num_stitches'
Mistake 3: Wrong Order of Arguments¶
Arguments are assigned to parameters in order, so the order matters! And that's why documentation is helpful.
# Correct order: width, length, stitches_per_inch, rows_per_inch
calculate_fabric_size(40, 200, 5, 6)
print()
# Wrong order - mixing up the gauge values will give wrong results!
calculate_fabric_size(5, 6, 40, 200) # This doesn't make sense!
Your fabric will be approximately 8.0 inches wide and 33.3 inches long. Your fabric will be approximately 0.1 inches wide and 0.0 inches long.
Try It Yourself!¶
Now it's your turn! Try writing a function called greet_knitter that takes a name as a parameter and prints a personalized greeting.
# Your code here
def greet_knitter(name):
# TODO: write the function body
pass
# Test your function
# greet_knitter("Alice")
So, to quickly summarize, we covered:
- Functions are a group of statements that perform a specific task
- We define functions using
def function_name(parameters): - We call functions using
function_name(arguments) - Parameters are variables in the function definition
- Arguments are values we pass when calling the function
- Functions can call other functions
- Docstrings document what a function does
Next time, we'll learn about return values, which allow functions to send results back to the code that called them, and variable scope, which explains which variables are visible where in your program. See you Thursday!