Open In Colab

Assignment 2: Functions, conditional statements, and iteration in Python

Congratulations on completing your first Python assignment! You’ll be progressively building on those core skills throughout the semester. As before, click File > Save a copy in Drive to save your assignment in your personal Google Drive, and rename the notebook by replacing “Copy of” with your last name.

In this week’s notebook, you are first going to learn about functions. Functions are in some ways the bread-and-butter of Python programming. They make code re-usable, as they allow you to apply your code in other contexts and circumstances. You can think about functions this way: while variables store elements of your code (e.g. numbers, strings, lists), functions can store entire chunks of your code, which can in turn be re-used as necessary.

Functions include a definition and parameters, and are specified with def as follows:

def myfunction(parameter1, parameter2, parameter3): 
    """Your code goes here"""

The def command allows you to introduce a function definition. You then specify the parameters within parentheses following the function name, which are like variables that operate within your function. A colon (:) then follows the parentheses, and the contents of the function are found beginning on the next line as an indented code block (more on indentation soon).

After you’ve defined the function, it can be called by supplying arguments to the parameters you’ve defined, again within parentheses. For example:

myfunction(arg1, arg2, arg3)

So how does this all work? Let’s try it out!

At a very basic level, you can define a function without parameters - an empty function. This function will print whatever you’ve specified after it is called.

def print_five():
    print(5)

Now, we call the function:

print_five()

Notice what happened when we called our print_five function. As the function has no parameters, it will always give us back 5. Naturally, you’re going to want your functions to get more complicated than this - which is what parameters are for. Let’s try out a function that has a parameter and performs a basic mathematical operation.

def divide_by_two(x):
    print(x / 2)

divide_by_two(11)

The function itself is fairly straightforward. Our new function, divide_by_two, takes a number, which we are calling x. It divides x by two, and then prints the result for us. By adding a parameter, however, our function is now re-usable. Let’s try it out:

divide_by_two(55)
## Now it's your turn!  Write a new, empty function that prints your name when called.  Call the function.

The ‘return’ statement

In the above examples, we’ve created functions that print some result to our screen when called. Often, however, we want our functions to work as part of a larger workflow. This is accomplished with the return statement, which allows the output of functions to be assigned to a new variable.

For example, let’s try to assign the result of the divide_by_two function to a new variable:

y = divide_by_two(12)

And when we ask Python for the value of y:

y

We don’t get anything back. Strictly speaking, y now holds a special value called None, which is what a function gives back when it has no return statement - run print(y) to see it. Now, let’s try modifying the function with return used instead of print.

def divide_by_two(x):
    return x / 2

y = divide_by_two(12)

y
## Now it's your turn!  Write a new function that subtracts 7 from a number
## and returns the result.  Assign the result of the function to a new 
## variable (your argument can be any number you want).

Python and whitespace

You may have noticed that the statements that follow the function definitions above are indented. Python code obeys whitespace for code organization. In many languages, functions, loops, and conditional logic (which we’ll get to below), etc. are organized with curly braces, like the equivalent R code below:

divide_by_two <- function(x) {
    return(x / 2)
}

The R code, like in many languages, is organized in relation to the position of the curly braces, not the positioning of the code itself per se. In Python, curly braces are not used in this capacity. Instead, code is organized by indentation and whitespace. Within a function definition, for example, the code to be executed by the function call should be indented beneath the line that contains the def statement. The Python convention is four spaces; Colab indents with two spaces by default. Either works, as long as you are consistent within a block of code. In Colab and most other Python development environments, the software will already know this and indent your code for you when you press Enter after the colon. Without proper indentation, however, your code will not run correctly.

def divide_by_three(x):
return x / 3

Named arguments

Later in the semester, we’ll get started using external libraries to do our work, which have many pre-built functions for accomplishing data analysis tasks. Many of these functions are very flexible - which means they have a lot of parameters! In turn, it can be helpful to keep your code organized when you are working with multiple parameters in a function.

There are a couple ways to supply multiple arguments to a function in Python:

  • In the order they are specified in the function definition;
  • As named arguments, in which both the parameter name and the argument are invoked.

I’ll explain this further. Let’s define a function, subtract, that takes two numbers and will subtract the second number from the first.

def subtract(x, y):
    return x - y

Now, we call the function by supplying two arguments to it:

subtract(11, 7)

We could get the same result by supplying named arguments to the function, which take the form of parameter = argument:

subtract(x = 11, y = 7)

Flipping the order of the arguments will give us a different result unless we name the arguments accordingly. Take a look:

subtract(7, 11)
subtract(y = 7, x = 11)

Be careful when mixing named and unnamed arguments, however. When arguments are unnamed, Python assumes that you are supplying them in the order of the corresponding parameters in the function definition. As such, you can mix named and unnamed arguments, but unnamed arguments cannot follow named arguments in a function call.

subtract(11, y = 7)
subtract(x = 11, 7)

Default values

A parameter can also be given a default value in the function definition. If you don’t supply an argument for it, the function uses the default; if you do, your argument wins. This is why you can call many library functions without supplying every parameter - most of them have defaults.

def subtract(x, y = 1):
    return x - y

subtract(11)          # y defaults to 1
subtract(11, y = 7)   # override the default with a named argument

Docstrings and documenting your code

You’ve already learned how to use comments to document your code with the # operator, like this:

# I just commented out this line!

Comments are generally best used for small descriptive statements about your code, or notes to yourself regarding something you’d like to remember. More formal documentation of your code - e.g. what your functions are supposed to do - are best handled through docstrings. Docstrings are enclosed in triple quotes ("""), and can go beneath the function definition to explain its components. Let’s try using a docstring to explain, in basic terms, the contents of our subtract function.

def subtract(x, y):
    """
    Subtract one number from another.

    Parameters:
    -----------
    x: The number you would like to subtract a quantity from (the minuend).
    y: The quantity you would like to subtract from x (the subtrahend).
    """

    return x - y

You’ve now created a new function, subtract() with associated help documentation. To view the documentation, you can use the built-in help() function to print the docstring, or type the function name followed by a question mark to pop up the documentation at the bottom of the screen. Try it out!

help(subtract)
subtract?
# Now it is your turn!

# Create a new function, "multiply", that multiplies two numbers together.
# Write a docstring in the function definition that describes what the function does,
# and explains the parameters to the user.
# Run your function and call it to test it out.

Booleans and conditional statements

The above functions we’ve written perform the same operations for any input values that are passed to them. When writing programs, however, you’ll often have to design them to be flexible to user input where outcomes may vary based on what users supply to the program. In regards to data analysis, you might want to produce a particular type of chart if the data are structured a certain way, which wouldn’t work in another scenario. You can build this type of logic into your programs with conditional statements.

Before we get into this, however, I want to introduce booleans in Python. You may have heard this term before in other classes. Booleans in Python are the values True and False; they can be used to evaluate, understandably, if a condition is true or not. For example:

3 > 2
2 > 3
2 == 3
2 != 3

In the above lines of code, we used basic mathematical operators to see if conditions are true or false. When we asked Python if 3 is greater than 2, it told us it was True; when we asked it if 2 is greater than 3, it said False. Such true/false logic can be used with conditional statements in your code. To do this, you’ll often use conditional operators, which you’ll be familiar with from basic mathematics, possibly with a couple exceptions. Conditional operators in Python are as follows:

  • < Less than
  • > Greater than
  • <= Less than or equal to
  • >= Greater than or equal to
  • == Is equal to
  • != Is not equal to

As humans, we use conditional logic all of the time without really thinking about it. For example, if I am hungry, I’ll probably go eat something; otherwise, I won’t. Such logic can be expressed in Python as a series of if, elif (which means else if), and else statements.

Let’s try to express this now in code in basic terms. This is made-up code, so we don’t need to understand everything, but here are the basics of it. We define a function, hungry, that measures the biological/psychological properties that govern hunger reflexes in the human body. We say, hypothetically, that a hunger index value encapsulating these properties that exceeds 100 means that a person is hungry; in turn, the function returns True. Otherwise, it returns False.

def hungry(hunger_index):
    if hunger_index > 100:
        return True
    else:
        return False

hungry(103)
hungry(88)

We can then pass a variable kyle, which represents my hunger index, to the function. The function then checks to see if I am hungry, and returns True if I am, and False otherwise. These results can then be embedded in other workflows, in which you program Python to perform different tasks depending on the function results. For example:

if hungry(kyle):  
    eat()
else: 
    stay_put()

As you can see above, this can be simplified in Python as the code if hungry(kyle) checks to see if the returned value is True. If I am hungry, the code calls an unseen eat() function telling me to go eat; otherwise, it tells me to stay put with an unseen stay_put() function.

Simple enough, right? Not quite. I’m hungry right now, but I’m not currently eating. Why is that the case? Well… I have to get this assignment written for you! While I am hungry, my level of hunger is not so debilitating that I am unable to work, and I know that finishing my work is a higher priority than eating at the moment. In reality, there are countless conditions that influence whether we eat or not beyond simply how hungry we are. For example - is there food accessible? Are you in a place where it is socially acceptable to eat? Perhaps you are at a party where pizza is available - you aren’t hungry, but you eat it anyway? Your brain processes all of this conditional logic at once as you make decisions, so we aren’t always used to thinking explicitly about conditionals in such discrete ways. However, to get a computer to understand this, you have to make it explicit in your code!

The hungry function we defined above used conditional logic, telling Python to return True or False depending on the value of the hunger index. Note that the if statement starts on a new indented line of code beneath the function definition, and itself is followed by a colon; the code associated with the if statement then follows on another, indented line of code. The same goes for the else statement beneath it. You should be getting a sense now of style rules in Python, and the importance of whitespace for code organization, which applies to conditional statements as well.

Now I’d like you to try to replicate this yourself! Define a function called greater_than_two that checks to see if a number is greater than 2 or not. Call the function.

### Your code goes here!

Loops

In programming, you will often want to do an operation multiple times, or carry out some process over a list of values that you’ve created. To do this, you will frequently turn to loops.

The most commonly used loops in Python are for and while. A for loop carries out an action, or set of actions, for every element that you tell it to. Let’s give it a try in very simple terms. I’ve created a list of teams in the Big 12 below; we’ll loop through this list with for and concatenate another string to each element.

big12 = ["TCU Horned Frogs", "Baylor Bears", "Texas Tech Red Raiders", "Oklahoma State Cowboys", 
         "Kansas Jayhawks", "Kansas State Wildcats", "Iowa State Cyclones", "West Virginia Mountaineers",
         "Cincinnati Bearcats", "Houston Cougars", "UCF Knights", "BYU Cougars",
         "Arizona Wildcats", "Arizona State Sun Devils", "Colorado Buffaloes", "Utah Utes"]
for team in big12: # You can read this here as "for every team in the Big 12....
    print(team + " are in the Big 12.") # ... print the name of the team along with "are in the Big 12."

A couple notes about the above code. Notice that I’m using team as a temporary variable that will successively store each value of the list as we loop through it. This variable, team, takes on each value of the list in turn; and presently stores the last value of the list.

team

As such, this is how the for loop works. for tells Python to evaluate every element of the list big12; these elements will be represented by the variable team. The loop then walks through the list big12 and prints the specified text for every element in the list. This for loop is equivalent, then, to:

print("TCU Horned Frogs are in the Big 12")
print("Baylor Bears are in the Big 12")
print("Texas Tech Red Raiders are in the Big 12")

# ... and so forth

but you don’t really want to do that manually, especially when you might have thousands of strings to evaluate. In turn, the for loop does the work for you, which is one of the main benefits of programming.

Also, I’d like you to notice the structure of the loop. Like functions, loops obey whitespace rules for code organization, and will fail if your code is not properly indented. The first line of the loop - in this case for team in big12 - is not indented, and is followed by a colon. Everything else contained in the loop is found in an indented block on the line(s) following the for loop call. As with functions, your loop will fail without proper indentation.

for team in big12: 
print(team + " are in the Big 12.")

Now let’s say you want to create a new list of the “…are in the Big 12” elements. There are a couple ways to do this. In the first, you define an empty list object, then populate the list with the for loop and the append method, as shown below.

teams = [] # The empty brackets designate an empty list, which you'll fill up with the loop.

for team in big12:
    teams.append(team + " are in the Big 12.")

teams

You can also do the same thing with something called list comprehension. List comprehension allows for the creation of a new list with one line of code. In this case, the for loop is embedded within the statement you make. Below is an example:

teams2 = [team + " are in the Big 12." for team in big12]

teams2

You can use methods and functions in your list comprehension as well, as in the example below:

swap = [name.swapcase() for name in big12]

swap

Loops also work for strings - though in this case the loop will iterate through each character contained in your string. An example:

tcu = "Texas Christian University"

idx = 0
for character in tcu:
    print("The index of " + character + " is " + str(idx))
    idx = idx + 1

The while loop

for is not the only loop you’ll come across in Python. You can also specify a while loop, which will run a loop until a given condition is satisfied. I’ll give you an example below. Let’s say you only want to return the first five elements of your list. While you could do this with indexing (which is simpler), let’s try the while loop instead.

newlist = []

i = 0

while i < 5:
    newlist.append(big12[i])
    i = i + 1

newlist

Have a look at what we did. We created an empty list, and set a new variable, i, to 0. We then looped through the elements of the list index by index, adding 1 to i with each run of the loop, and then stopping the code when i became equal to 5. In turn, we got back the elements of the big12 list indexed 0 to 4, which are the first five elements.

Be careful with while loops, however. If you don’t specify them correctly, you can get stuck in an infinite loop, which will not stop! For example, if we had not continued to add 1 to i, the loop would have never stopped and would have kept iterating through the list. In Colab, this will cause your notebook to hang while it tries, forever, to finish the loop. If that happens, click the stop button next to the running cell to interrupt it. I’ve been there a few times…

The range function

Sequences of numbers in Python can be generated by typing them out, as you’ve learned how to do in this class. However, they can also be produced by the built-in range function, which can simplify things for you significantly if you need to generate a long list of integers. range has three parameters: start, which is the first integer you want to start with; stop, which is the first integer you don’t want to include, and step, which optionally specifies the integer interval. For example:

x = range(1, 21, 5)

x
type(x)

We’ve created a range object which conforms to the arguments we supplied to the range function. Notably, the range object can be looped through:

for y in x:
    print(y)

If need be, your range object can also be converted to a list with the list function.

list(x)

Exercises

As with last week’s notebook, I’ll ask you to complete a series of small exercises below to get full credit for this week’s assignment. Some of these exercises will build upon the skills you learned last week - so be prepared! You are welcome to work together on these exercises, and you can use Colab’s embedded AI assistant to help you with them. My recommendation: try to complete the exercise first without AI, then only use AI when you get stuck or to help provide an explanation.

Exercise 1: Explain, in your own words, the difference between using print and return for the output of a function. Write your response below, in this Markdown cell.

Exercise 2: Write a function called make_big that takes a lowercase string as input and prints the string with all capital letters. Create a new variable and assign to it a string with all lowercase letters, then call the make_big function with that variable as its argument.

# Your answer goes here!

Exercise 3: Write a function called first_word that takes a list of character strings as input and returns the first element of the list in alphabetical order. For example, your function should work like this:


students = ['Mary', 'Zelda', 'Jimmy', 'Jack', 'Bartholomew', 'Gertrude']

first_word(students)

'Bartholomew' # The result

Hint: You’ll need to first sort your list in the function to accomplish this, then identify the first element. Within a function, it is a good idea to use multiple lines of code to separate out the different steps. Just make sure all the code that belongs to the function is indented!

After you’ve written the function, run it by supplying the list teams to your function as an argument to test it. Before you run it, write down in a comment which team you expect to come back first. Then check.

teams = ['Stars', 'Cowboys', 'Rangers', 'Mavericks', 'FC Dallas']

# Your code goes below!

Exercise 4: Explain, in your own words, the difference between a for loop and a while loop. Write your response below in this Markdown cell.

Exercise 5:

You’re the treasurer of a TCU student club, and you’re in charge of ordering pizza for the club’s meetings this semester. RSVPs come in before each meeting, and the club gives you a budget. Your job is to figure out how many pizzas to order.

Doing this by hand once is easy. Doing it every meeting, for every club on campus, is exactly the kind of thing a function is for. And as with last week, you’re going to work with an AI assistant to build it. The difference this week: last week you checked the assumptions the AI made. This week, you make the assumptions, and then check that the code follows them.

Run the cell below to load the RSVP counts for this semester’s meetings, along with the budget and the price of a pizza.

rsvps = [18, 6, 35, 24, 11, 52]   # one number per meeting, in order

budget = 150     # dollars per meeting
price = 15       # dollars per large pizza

print(len(rsvps), "meetings loaded")

Part 1: Make a plan. Before you write any code, decide on your ordering rules and write them down here. You’ll need to decide at least the following:

  • Do you think everyone who RSVPs will show up? Will anyone show up who didn’t RSVP?
  • How many slices does a person eat?
  • How many slices are in a pizza?
  • You can’t order part of a pizza. Which way do you round?

There is no single right answer to any of these. Just make a choice, and say why in a sentence.

Then, using your rules, work out by hand how many pizzas you would order for the first meeting (18 RSVPs), and what it would cost. Write those two numbers down.

Double-click and answer here.

Part 2: Build. Ask Gemini or another AI assistant to write a function called pizzas_needed that follows your rules and returns the number of pizzas to order. Give it your rules from Part 1 - you can copy and paste them.

You’ll also need to decide what the function’s parameters are. Which quantities change from meeting to meeting, and which are rules that belong inside the function? (Think back to the tutorial: a parameter like slices_per_person can have a default value, which you override with a named argument when you need to.)

Paste the code below, then call your function for the first meeting. Does it give the same answer you worked out by hand?

# Paste and run the code Gemini helped you write, then call it for the first meeting.

Look at the function closely.

  1. Find each of your rules from Part 1 in the code. Which line carries out which rule?
  2. Is there any rule in the code that you did not write down? What is it?

Double-click and answer here.

Part 3: Try every meeting. Write a for loop that calls pizzas_needed for every meeting in rsvps and prints the RSVP count, the number of pizzas, and the cost.

Then look at the results like a treasurer, not a programmer. Would you actually place these orders? Pay special attention to the smallest meeting and the largest one.

Then change one assumption - say, three slices per person instead of two - and run the loop again. How much did the orders and the costs change?

# Your loop goes here.

Double-click and write down what you noticed.

Part 4: The complication. Bad news from the club president: the budget for the last meeting - the one with 52 RSVPs - has been cut to $75. That’s five pizzas.

You have to decide what the function should do when the order doesn’t fit the budget: order what the budget allows and risk running out, recommend asking for more money, or something else. Decide on a policy, then revise your instructions to Gemini so the function follows it. Paste and run the revised code below, run it for the last meeting, and explain in a few sentences what your policy means for the people who show up.

# Paste and run the revised code, then run it for the last meeting.

Double-click and answer here.

To submit your assignment, click the Share button and share your assignment with kwalkertcu@gmail.com.