Goals
Upon successful completion of this lab, you should be able to:
- Trace the execution of programs that use functions.
- Define and call your own functions.
- Break programs down into simple functions.
Setup and Practice
Setup
- If your lab machine was not started in MacOS when you went to use it, reboot it to MacOS.
- Follow these procedures to mount your blue home-directory on the local machine and create a PyCharm project (call it Lab08). You will use PyCharm in the next part, but, it is important that you set up your project now so that if you run into any issue, we can resolve them quickly.
Practice: String Methods
In this part, you'll use the the online Python 3 tutor to practice splitting strings into lists.
- Predict what this code will do and answer Questions 1 and 2 in your writeup:
x = 'I am a string' z = x.split() print(z[0]) print(x)
- Predict what this code will do and answer Question 3 in your writeup:
x = 'I am a string' for w in x.split(): print(w, end="") print()
- Predict what this code will do and answer Question 4 in your writeup:
x = 'I am a string' y = x.split() print(len(y))
Practice: Populating Lists
For this part of the lab, you'll use the online Python 3 tutor to build lists using append
.
- Answer Question 5 in your Moodle writeup by analyzing this code, using Python Tutor if needed:
L = [] for i in range(3): L.append(i)
- Answer Question 6 in your Moodle writeup by analyzing this code, using Python Tutor if needed:
L = [] for j in range(5): L.append(j ** 2)
- Answer Question 7 in your Moodle writeup by analyzing this code, using Python Tutor if needed:
A = ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec'] B = [] for month in A: B.append(month)
Note: there are more efficient ways to do what this code does --- this is just an example that uses
append
.
Practice: Functions
- Enter the following code into the tutor:
import math def PrintHello(): print("Hello!") # A def PrintGoodbye(): print("Goodbye!") # B def HiFive(): print("HiFive!") # C return 5 def main(): print("Calling PrintHello") # D PrintHello() print("Calling HiFive") # E value1 = HiFive() print("Result of HiFive:", value1) # F main()
- Answer Question 8 in your Moodle writeup.
Part A: Draw national flags
In this part of the lab, we will write a program that draws the national flags of Japan, Bangladesh, France, Russia and Sudan and displays them one at a time. This is a large chore that can be cleanly broken-down into smaller steps with functions. We will write a separate function to draw each flag and grow our program incrementally by adding more and more functions to it. Your final program will draw- In PyCharm, create a new Python source code file named lab08a.py:
""" Program: CS 115 Lab 8a Author: Your name Description: This program displays national flags. """ from graphics import * def draw_japan_flag(flag_width): '''Draws the national flag of Japan in a graphics window. Args: flag_width (int): The width of the window. Returns: None ''' flag_height = 2 / 3 * flag_width circle_diameter = 3 / 5 * flag_height # Open a new graphics window with the title 'Japanese flag', # the width passed by the caller, and the calculated height win = GraphWin('Japanese flag', flag_width, flag_height) # Set the window background to white win.setBackground('white') # Set up the red circle. flag_center = Point(flag_width / 2, flag_height / 2) circle_radius = circle_diameter / 2 # Create a circle that is centered in the middle of the flag # and has the specified radius circ = Circle(flag_center, circle_radius) # Turn that circle red circ.setFill('red') # the inside of the circle circ.setOutline('red') # the line around the circle # Actually draw the circle circ.draw(win) # Close? input('Press ENTER to close the flag window.') win.close() # --- Our main function --- def main(): draw_japan_flag(600) # Draw a Japanese flag with a width of 600 pixels main()
Run your program. You should see a graphics window pop up with the Japanese flag:
- Read through the code for the draw_japan_flag() function. Try to understand all of the comments.
- In
main
, change the 600 to other values (200, 800, etc.), run the program, and see what changes. - Answer Question 9 in your writeup.
Over the next few steps, you will write a new function called
draw_bangladesh_flag
. This function will draw the flag of Bangladesh:Since the Bangladesh flag is similar to the Japanese flag, you can use your old code as a basis for your new code. Just below your docstring and import statement, make a new copy of your entire
draw_japan_flag
function, and name this copydraw_bangladesh_flag
.Here is what the structure of your code should look like:
""" This is just the structure of your code. Don't copy this into your program! """ import statements def draw_bangladesh_flag(flag_width): # code for Bangladesh flag (we will do this next) def draw_japan_flag(flag_width): # code for Japanese flag def main(): draw_japan_flag(600) main()
Now, you will modify the
draw_bangladesh_flag
function so it draws the Bangladesh flag. The specifications for the Bangladesh flag (source) are the following:Bangladesh Flag Specification
- The flag height is 3/5 of the width.
- The background is dark green. (Use the string 'DarkGreen' when setting its color.)
- The circle is red.
- The radius of the circle is 20% of the width of the flag.
- The center of the circle is at 9/20 of the width and half of the height of the flag.
draw_bangladesh_flag
so that it draws the Bangladesh flag correctly.Inside your
main()
function, comment out the call to thedraw_japan_flag
function, and add a call to thedraw_bangladesh_flag
function, like this:#draw_japan_flag(600) draw_bangladesh_flag(600)
By doing this, we are demonstrating a useful debugging practice: we are testing the new function separately from the rest of the code, by commenting-out things during development.- Run your program. Verify that it draws the Bangladesh flag. Change the 600 to other values, rerun your program, and be sure that the flag resizes correctly.
- Once it is working, be sure to go back through
draw_bangladesh_flag
and update the docstring so it is accurate. Next, we will draw flags that have stripes. We'll start with the national flag of France:
To draw the stripes, we will just draw three rectangles. Recall: to draw a rectangle, we have to specify the coordinates of two opposite corners.
- Insert the following code at the top of your program, just below the import statement. The def lines should be against the left margin, just like with the other two flag functions.
def draw_stripe(window, top_left, bottom_right, color): '''Draws a rectangle in the graphics window. Args: window (GraphWin): The window used for drawing. top_left (Point): The coordinates of the top left corner. bottom_right (Point): The coordinates of the bottom right corner. color (str): The color to make the rectangle. Returns: None ''' stripe = Rectangle(top_left, bottom_right) stripe.setFill(color) stripe.setOutline(color) stripe.draw(window) def draw_france_flag(flag_width): '''Draws a French flag in the graphics window. Args: flag_width (int): The width of the window. Returns: None ''' flag_height = 2 / 3 * flag_width stripe_colors = ['DarkBlue', 'white', 'red'] stripe_width = flag_width / len(stripe_colors) # Open a new graphics window with the title 'French flag', the # width provided by the user, and the calculated height window = GraphWin('French flag', flag_width, flag_height) # Draw the blue stripe # The top left of the stripe is the top left of the window blue_stripe_top_left = Point(0 * stripe_width, 0) # The bottom right of the stripe is 1/3 of the way across # the flag, and all the way to the bottom. blue_stripe_bottom_right = Point(stripe_width, flag_height) draw_stripe(window, blue_stripe_top_left, blue_stripe_bottom_right, stripe_colors[0]) # TODO: Complete the below code # Write similar code for the white and red stripes. # Wait for keyboard, to close the window input('Press ENTER to close the flag window.') window.close()
We are not going to modify the code yet. As it is currently written, it should make a flag with a blue stripe. - In
main
, comment out the call todraw_bangladesh_flag
and add the line:draw_france_flag(600)
- Run your program and verify that you see a dark blue stripe.
- Answer Question 10 in your writeup. You are strongly encouraged to use pencil and paper to answer this question.
- Now, complete the code: based on the code for the dark blue stripe, write code to draw the white stripe (even though the background may already be white) and write code to draw the red stripe.
- Answer Question 11 in your writeup.
- Based on your answers to Question 11, re-write your
draw_france_flag
code to draw the stripes using a loop. The loop should look something like this:for i in range(...something...): stripe_top_left = Point(...something...) stripe_bottom_right = Point(...something...) draw_stripe(...four things...)
Next, you will draw the Russian flag. To do so, make a new copy of the
draw_france_flag
function, and rename it todraw_russia_flag
. You will adapt that code to draw the national flag of Russia:Here are the specifications for the Russian flag:
Russian Flag Specification
- The ratio of width to height is the same as the French flag.
- For the stripe colors, use 'white', 'blue', and 'red'.
- The three stripes are all equal in size (1/3 of the height of the flag).
- Answer Question 12 in your writeup.
- Revise the
draw_russia_flag()
function to draw the Russian flag. Hint: you should not need to change the definition ofdraw_stripe()
. - In
main
, test your code by calling thedraw_russia_flag()
function and passing it different values of the window width. - Make a copy of your
draw_russia_flag()
function, and name the copydraw_sudan_flag()
. - Modify your
main()
function to calldraw_sudan_flag
. Over the next few steps, you will modify this function to actually draw the national flag of Sudan:
- First, just go through the definition of
draw_sudan_flag
and update the docstring, replacing Russia with Sudan in anticipation of your changes. - Here are the specifications for the Sudanese flag (source):
Sudanese Flag Specification
- The height of the flag is 1/2 of its width.
- For the stripe colors, use 'red', 'white', and 'black'.
- The triangle is dark green (use 'DarkGreen').
- The triangle is an isosceles triangle (2 equal sides) and extends 1/3 of the way across the flag.
- Answer Question 13 in your writeup.
You can use the Polygon object to draw the triangle. To make a Polygon that is shaped like a triangle, just pass it Point objects for each vertex of the triangle. The graphics library will connect the vertices in the order listed, plus one more connection between the last point and the first point. For example:
triangle = Polygon(Point(50, 50), Point(100, 100), Point(25, 100))
The above code draws a triangle with vertices at (50, 50), (100, 100), and (25, 100).- Write code to draw the Sudanese flag's triangle using Polygon.
- Save your work as the file lab08a.py.
- Continue to Part B.
Circles: Japan and Bangladesh
Rectangles: France and Russia
Other Polygons: Sudan
Part B: Draw user-specified flags
In this part, we will build a more functional program that calls the functions we wrote in Part A. It will get input from the user to draw specific flags, in a specific order.Instructions
- Save lab08a.py file as lab08b.py file and work on the lab08b.py file from this point onwards.
In your
main
function, before calling any of your other functions, write code to ask the user for the width of the graphics window:Window width: 700
When drawing a flag, pass the user's chosen value as the argument for the flag_width parameter, instead of 700.You may assume that the user always enters an integer. However, if that integer is not between 100 and 1000 (inclusive), then you should print an error message and exit the program.
To cause a program to exit, you call the
exit
function (provided by thesys
module). The step-by-step instructions for doing this are:- Between your docstring and your first function definition, add the line
import sys
- Where you want to exit, call
sys.exit()
. You may pass it an optional string argument which it will display as an error message before exiting. For example:sys.exit('Error: Window size should be between 100 and 1000.')
- Between your docstring and your first function definition, add the line
In
main
, after prompting the user for the window size, write code to print the following menu:Which national flag(s) do you want to draw? - Japan - Bangladesh - France - Russia - Sudan Name your country:
Modify
main
to read the user's input for the country. If the user's input is one of the five country names, then call the function that draws that country's flag, and pass it the chosen width. Otherwise, print an error message.To do this, treat the user's input is case-insensitive. In other words, the inputs JAPAN, Japan, and even JaPaN should all draw the Japanese flag. (Hint: this should simply your logic to call the appropriate function, since you will not need to manually list every possible combination of upper- and lowercase letters.)
Next, you will modify your program so that the user can type multiple country names on a line. For example:
Which national flag(s) do you want to draw? - Japan - Bangladesh - France - Russia - Sudan Name your countries: jaPan FRANCE
Here's what you will need to do:
- Split the user's string input into a list of strings.
- For each element in that list, make it case-insensitive, and call the appropriate flag function. You will want to move your if-statements into your new loop.
- When your code runs, the first flag in the user's list should pop up in a new window. When you hit enter in the text window, the graphical window should close and the next flag (if any) should pop up.
Print an error message for each word the user typed that does not name a valid country. For example:
Name your countries: JapaN CS115istan CS115land Russia Press ENTER to close the flag window. Error: CS115istan is not a valid country. Error: CS115land is not a valid country. Press ENTER to close the flag window.
- Demo.When your code is working, call an instructor over to demo.
- Continue to Part C.
Part C: Refactor: geometric calculations
In this part, you will write the code that you actually turn in for grading. The code you will write is a refactored version of logic from a prior lab. Refactoring is just a fancy term for rewriting old code, usually to improve its organization, enhance its generality or increase its efficiency.
In this case, we will refactor the Lab 2 code that did various geometric calculations (the area of a square, volume of a cube, etc) so that it is organized into sensible functions.
Instructions
- Create and open a new Python source code file named lab08c.py:
""" Program: CS 115 Lab 8c Author: Your name Description: This program computes geometric quantities. """ import sys import math def get_numeric_val(): '''Prompts the user for a number. Exits if the user does not enter a positive value; otherwise, returns the value they entered. Returns: float: The number entered by the user. ''' num = float(input('Enter a positive numeric value: ')) if num <= 0: sys.exit('Error: that number was not positive.') return num def get_menu_choice(): '''Prints a menu and returns the user's selection. Returns: str: A single character ('q', 'a', 'b', 'c', etc). ''' pass def compute_square_area(side): '''Computes the area of a square. Args: side (float): The side length for the square. Returns: float: The area. ''' pass def compute_circle_area(radius): '''Computes the area of a circle. Args: radius (float): The radius length for the circle. Returns: float: The area. ''' pass def compute_cube_volume(edge): '''Computes the volume of a cube. Args: edge (float): The side length for the cube. Returns: float: The volume. ''' pass def main(): menu_choice = get_menu_choice() # Get the user's first choice while menu_choice != 'q': user_num = get_numeric_val() # Get the side length (etc.) if (menu_choice == 'a'): print('The area of a square with side length ', user_num, ' is ', round(compute_square_area(user_num), 5), '.', sep="") elif (menu_choice == 'b'): print('The area of a circle with radius length ', user_num, ' is ', round(compute_circle_area(user_num), 5), '.', sep="") elif (menu_choice == 'c'): print('The volume of a cube with edge length ', user_num, ' is ', round(compute_cube_volume(user_num), 5), '.', sep="") menu_choice = get_menu_choice() # Get user's next choice main()
- Answer Question 14 in your writeup.
- Right now, some of your functions have empty definitions --- this is what
pass
means (it is a valid Python statement that has no effect). Replace the logic insideget_menu_choice()
with code to do the following:- Print this menu:
Would you like to a. Calculate the area of a square? b. Calculate the area of a circle? c. Calculate the volume of a cube? d. Calculate the volume of a sphere? e. Calculate the area of an equilateral triangle? q. Quit?
- Read in the user's selection and convert it to lowercase. You may assume that the lowercase version is always one of the 6 options listed in the menu.
- Use a
return
statement to return the user's choice to the function's caller.
- Print this menu:
- Fill in the logic for the function
compute_square_area
, so that it returns the area of a square whose side length isside
. Note:- Do not modify the
main
function. - Do not change the
def
line of this function. - Do not use
print
within this function. - You can refer to Lab 2 for all of the formulas in this part of the lab.
- It is possible to write this function in only one line of code (not counting the
def
line).
- Do not modify the
- Run your program. Here is a sample input/output sequence:
Would you like to a. Calculate the area of a square? b. Calculate the area of a circle? c. Calculate the volume of a cube? d. Calculate the volume of a sphere? e. Calculate the area of an equilateral triangle? q. Quit? A Enter a numeric value: 5 The area of a square with side length 5.0 is 25.0. Would you like to a. Calculate the area of a square? b. Calculate the area of a circle? c. Calculate the volume of a cube? d. Calculate the volume of a sphere? e. Calculate the area of an equilateral triangle? q. Quit? q
Fill in the function
compute_circle_area
so that it returns the area of a circle whose radius is radius.Note: Just as before, do not modify the
main
function; do not change thedef
line of this function; do not useprint
within this function.Fill in the function
compute_cube_volume
so that it returns the volume of a cube whose edge length is edge.Note: Again, do not modify the
main
function; do not change thedef
line of this function; do not useprint
within this function.Write a new function
compute_sphere_volume
that computes the volume of a sphere. Your function should take one parameter, radius, and return the volume of a sphere with that radius.Be sure to write a docstring for your function, modeled after the format of the other function docstrings provided in the lab.
Write a new function
compute_tri_area
that computes the area of an equilateral triangle. Your function should take one parameter, side, and return the area of an equilateral triangle with that side length.Be sure to write a docstring for your function, modeled after the format of the other function docstrings provided in the lab.
- Add code to the main function to call your new functions and print their output when the user selects 'd' or 'e'. You should round their outputs to 5 decimal places.
- Test your program thoroughly. Here is a sample input/output sequence:
Would you like to a. Calculate the area of a square? b. Calculate the area of a circle? c. Calculate the volume of a cube? d. Calculate the volume of a sphere? e. Calculate the area of an equilateral triangle? q. Quit? c Enter a numeric value: 2 The volume of a cube with edge length 2.0 is 8.0. Would you like to a. Calculate the area of a square? b. Calculate the area of a circle? c. Calculate the volume of a cube? d. Calculate the volume of a sphere? e. Calculate the area of an equilateral triangle? q. Quit? d Enter a positive numeric value: 5 The volume of a sphere with radius length 5.0 is 523.59878. Would you like to a. Calculate the area of a square? b. Calculate the area of a circle? c. Calculate the volume of a cube? d. Calculate the volume of a sphere? e. Calculate the area of an equilateral triangle? q. Quit? e Enter a positive numeric value: 12.2 The area of an equilateral triangle with side length 12.2 is 64.44961. Would you like to a. Calculate the area of a square? b. Calculate the area of a circle? c. Calculate the volume of a cube? d. Calculate the volume of a sphere? e. Calculate the area of an equilateral triangle? q. Quit? q
- Demo.When you are convinced that your program is working correctly, demo it for an instructor.
- Continue to the next part to submit your program.
Assignment Submission
Instructions
- Answer the last question (#15) in your Moodle writeup. Review your answers, and then click the "Next" button at the bottom of the quiz. Once you do that, you should see a "Summary of Attempt" screen.
- Click the "Submit all and finish" button.
Warning: You must hit "Submit all and finish" so that your writeup can be graded! It is not submitted until you do this.
Once you have submitted your quiz, you should see something similar to this at the top of your Moodle window. The important part is that the State shows up as Finished.
Please leave this tab open in your browser. - Click on the "Lab 8 code" link in Moodle and open in a new tab. Follow the instructions to upload your source code (lab08c.py) for Lab08. You could either browse for your code or, using a finder window, drag and drop your lab08c.py from your cs115/Lab08 folder to Moodle. You should subsequently see a dialog box which indicates 'Submission Status' as 'Submitted for grading'. Leave this tab open in your browser.
- With these confirmation pages open in your browser, you may call an instructor over to verify that you have completed every part of the lab. Otherwise, you are done!