Lab 2: Python Functions, and Testing

If you find typos or problems with the lab instructions, please let us know.

Overview

The main purpose of this lab is to practice the basics of Python.

You will work with your pair partner on the same code. As such, you don't each have your own document. Instead, one of you will be coding at any given time, and the other will be supporting: you two should be working together the entire time.

Follow the driver-navigator model and switch roles frequently.


Learning Objectives

The most important thing we want you to get out of this lab is the ability to …

1. Create a Google Colab

For this lab, you will be working with your Pair Partner to write code in a Google Colab notebook, which you will save to a GitHub repository.

The procedure to do this is explained at the following page: Google Colab and GitHub 

After you followed this procedure, you should now have a Google Colab notebook, named spis23-lab02-Alex-Chris.ipynb (but with your first names instead of Alex and Chris), that is linked to a shared repo on GitHub. You and your Pair Partner will sit next to each other and work on this Google Colab notebook together, but only one of you should open it, and you should both work on the same computer.

We recommend writing each part of this lab in its own cell in the Google Colab notebook, though you are welcome to reorganize your code however you think is cleanest once you finish the lab.

In the first cell of your Google Colab notebook, paste the Python code below.

# The goal of this program is to practice Python constructs
def sumTwo(a,b):
  c = a + b
  return c

x = sumTwo(3,5)
print(x)

Have a look at the code. It consists of a function that returns the sum of two input arguments; this function is called and the result is printed. This should make sense to you based on what you’ve learned about Python so far. Now let’s run the program. Is your output what you expect?

Next, push the code to GitHub. Only one of you needs to do this.

We will be using this workflow throughout SPIS. Make sure it all makes sense  and don't hesitate to ask questions.

2. Asking the User for Input

Now that you have gone through the workflow, it is time to do some more coding. Remember to switch between who is the driver and who is the navigator frequently.  

Create a new cell in your Google Colab notebook (the "+ Code" button at the top-left), and in this new cell, add a new function: getNumber, as shown below.

def getNumber():
  symbols = input("Enter a digit: ")
  number = int(symbols)
  return number

Let’s go over this code together. The first line inside getNumber() uses the built-in Python function input() to ask the user to type in something. This could be a single symbol (e.g., ‘a’), a word (e.g., ‘apple’) or even an entire novel. You can keep typing until you hit the Return/Enter key. We will assume the user only types in a single decimal digit (so one of ‘0’,’1’, … ‘9’), as this is what we asked the user to do (in the text that is between “ “ in the input-function).

However, Python interprets whatever is returned by the input-function as a string. A string is basically a collection of symbols. Since we only entered a single character (‘0’, ‘1’, .. ‘9’), this string would be a single symbol. The int()-function is used to convert this symbol to a corresponding number (if this is possible). For example, the symbol (letter) ‘1’ gets converted to the number 1. Why is this necessary? Well, even though they look the same to us, a computer needs to know whether you are talking about a symbol or a number, as it treats those differently. For example, it can multiply the number 1 by two, but what would it even mean to multiply the symbol ‘1’ by two? This is why we had to use the int-function to convert from a symbol to a number. Finally, now that we have a number, it is returned by the function.

Now, your task is to modify the getNumber function. It should repeatedly ask the user to enter a single digit, instead of only once. This means that you probably want to put the bit of code that asks for a user input in some kind of loop (remember while from lecture?) The number that is returned at the end of the function should be the combination of these individual digits. For example if the user enters 4, then 7, then 0 and then 9, the function should return the number 4709. You may wonder, if we are putting the user input inside a loop, how do we know when to stop? We will assume that as soon as the user answers with a number that is negative, you can stop asking (and ignore this negative number). So in the example above, the user would really enter 4, then 7, then 0, then 9 and then -1 (or any other number).

When you think you have your function, or even part of it, written, you should also test it. To do this, call the function in your code. This is similar how we called sumTwo. Test your function thoroughly to convince yourself it works correctly. Once this is done, you are ready to push your changes to GitHub.

3. Getting the Sum of the Digits

You are going to add one more function to your Google Colab notebook (also in its own new cell), called sumDigits. This function should take a single parameter, which is a multi-digit number. The result it returns is the sum of the digits of this number. For example sumDigits(236) should return 11, because 2 + 3 + 6 = 11.

def sumDigits(x):
  # You will need to complete this function
  return sum

Complete the code for this function. Two useful operations here are % (modulo; which returns the remainder of a division) and // (integer division; which divides the number and rounds it to the nearest integer). For example, 11//2 is 5 and 11%2 is 1.

Now you need to come up with an algorithm that lets you extract each individual digit from a number, so you can add these together. We will give you some hints, but it’s good to try and figure out the algorithm without them. Brainstorm with your partner. Don’t think about it on your own and then explain to the other person. Just start talking … let the ideas flow and see if you can really think together. This is a good exercise to get you in the habit to do this throughout your pair programming exercises. If one partner already knows the answer (maybe you’ve seen it somewhere before), don’t simply tell the other person. Let them figure it out gradually.

After you’ve really thought about it and you need some help, here are some hints. First, think about how you can use division and/or modulo to get the least significant (i.e., right-most) digit of a number. So, how can you extract 6 from 236? It is helpful to consider that our number system uses base 10 (hence the name “decimal”). Once you have that, how can you get the number that would result from chopping of the least significant digit? So, how can you get 23 from 236? If you are able to do this, how can you get the next digit? Is there a repeating pattern here? This would suggest once again … a loop.

Once you have figured out the answer, test your function for a few different values to convince yourself it works correctly. When you are done and your code works correctly, push the updated file to GitHub (as we did for the previous function you added). Verify on GitHub that indeed all your updates got pushed online.

4. Wage Converter

For this part, we are going to explore a bit more how you can approach testing your Python code … and give you a few more open ended coding challenges. Remember to switch driver-navigator roles frequently.

Getting started

Add this function to a new code cell in your Google Colab notebook. It uses the the gender wage gap in the United States to calculate a woman’s wage based on the corresponding male wage. 

def convertWageMtoW(mWage):
  wageGap = 0.182
  ratio = 1 - wageGap
  return mWage * ratio

The wage gap is defined as the difference between a man’s salary and a woman’s salary, expressed as a percentage of a man’s salary. In the United States the wage gap is 18.2%(at the time this lab was created). To calculate a woman’s salary, given a man’s salary using the wage gap we can multiply the man’s salary by 1-wageGap. You can learn more about the wage gap and see more data here.

To verify our code is correct, we need to test it, as we did before. More formally, this means that we need to define test cases. A test case is a combination of an input and the expected output. The first step to testing is to define these input-output pairs. We can use a calculator to do this, for example:

It’s good to try a range of inputs that are qualitatively different. Notice in the test cases above, we have selected one non-zero integer input, one decimal number and the number 0.

Add code to the program to verify these cases, and make sure the output is as you expect. If the cases do not give you the correct expected output, figure out what is wrong.

Now add two more test cases of your own.

When doing this testing, you might find that your implementation returns values that are almost correct, e.g. 0.000000000000000000001 for 0.0 or something similar. This is due to imprecision in the way Python represents floating point numbers and is completely normal. You should consider your results correct if they “very close” to the expected results.

What’s next? Push the changes up to GitHub.


Adding (and testing) additional functionality to your wage converter

Now, extend the functionality of your wageCalculator function so that:

You can use the data on this page, or any other data you find on the web in your functions.

Exactly what your extended function does is up to you and your partner, but it should still be focused on calculating salaries based on the gender wage gap. Here are some ideas for extensions, but feel free to come up with your own:


Comment your code

In a comment above your modified function, make sure you describe what it does. Include a description of what each parameter means, and what is returned.


Reorganize your code

Throughout this lab, we organized each part of the lab into its own cell. In practice, it is typically cleaner to separate each function into its own cell, and then have any code that calls the function in a separate cell under it. Try reorganizing the code you've written to make your notebook nice, clean, and easy to read/understand.

Also, one benefit of Google Colab notebooks compared to just writing basic Python code is that, in addition to code comments, we can use Markdown to write formatted text within our notebook! Add some Markdown cells to describe what your notebook is doing, what each function is doing, etc.


Test your code

Come up with several test cases. If any of your test cases fail, fix your code so that they pass. Make sure you test enough cases so that you are confident your code works in all cases. For example, if you take a country name as one of your parameters, what happens if the user enters a country your code does not know about? Does it behave as you expect?

If you get part-way done, and some of your tests pass, but not others, or you are in the middle of working when it is time for a break, that is STILL a good time to push to GitHub. Add the letters “WIP” to the start of your commit message so you know that this is not a finished product. “WIP” stands for “Work in Progress”. 

When your code is finished, it passes all your test cases and you have pushed your final version to GitHub, congratulations! You are done with this lab, but consider some of the additional challenges below …

Challenge Problem: Implement a Word-Guessing Game

Even though you have just started learning Python, you already have all the knowledge required to implement a fun little game you are probably familiar with. The goal of the game is to guess a word or phrase, by asking if a letter appears in it. You win by guessing all the letters before you made 7 mistakes (i.e., asked for a letter that did not appear). You can try out a version of this game here.

You can be creative in how you implement this game. The code below shows a few useful constructs. Note that the [] operation lets you extract individual characters from a string. Note that the indexing starts at 0 (this means the first letter is at position 0, the second one at position 1, etc.). Play around with this to figure out what it does.

a = "hello"
b = a[0]      # Get the 0th letter in a, in this case "h"
c = len(a)    # This gives the number of letter (length) of a

You can also make the game flexible, where the number of mistakes allowed is a parameter.

Challenge Problem: Password Security

Write a function called is_secure that has a single parameter password (a string) that returns the string "secure" if password is secure, or returns the string "insecure" if password is insecure. A password is considered to be secure if it has all of the following properties:

Here are some examples to demonstrate the functionality of the is_secure function:

Challenge Problem: String Rotations

The i-th rotation of a string is the result of shifting every character of the string i positions to the right, with characters at the end wrapping around to the beginning. For example, the 2nd rotation of the string "Burrow" is "owBurr". For any given string, the 0-th rotation is simply itself.

Write a function called rotate that has two parameters (a string word and an integer i) that returns the i-th rotation of word.

Here are some examples to demonstrate the functionality of the rotate function:

Challenge Problem: Welcome Message

Write a function called welcome that has has a single parameter name (a string) that prints the welcome message Hello, name! Welcome to SPIS! :) surrounded by a box of asterisks (*) with spaces separating the box and your text.

Here are some examples to demonstrate the functionality of the welcome function:

*************************************
*                                   *
* Hello, Niema! Welcome to SPIS! :) *
*                                   *
*************************************

************************************
*                                  *
* Hello, Curt! Welcome to SPIS! :) *
*                                  *
************************************

Challenge Problem: Triforce

In The Legend of Zelda, the Triforce is a sacred golden relic and is an iconic symbol of the series.

Write a function called triforce that has a single parameter num (an integer) that prints a Triforce composed of 3 centered triangles created with num asterisks (*) on the bottom row, num-2 asterisks on the next bottom row, ..., and 1 asterisk on the top row, surrounded by hyphens (-).

Here are some examples to demonstrate the functionality of the triforce function:

-----*-----
----***----
---*****---
--*-----*--
-***---***-
*****-*****

-------*-------
------***------
-----*****-----
----*******----
---*-------*---
--***-----***--
-*****---*****-
*******-*******

Additional Challenges

If you still have time, you can extend your code in some other way, read more about the wage gap, or anything else related to this lab! Or do some more picobot …