Advanced Topics in Computer Science

Unit 1 - Python Bootcamp

This course uses object-oriented Python to explore advanced topics in computer science. If you're coming into this class from a previous Computer Science course at Poly, you have either:

Either way, you're going to need develop a strong, working knowledge of object-oriented programming in Python before we can move on to the topics in this course.

Let's get started!

1.0. Overview

We're going to spend a few days covering the essentials of computing in Python:

  1. General information
  2. Output
  3. Comments and Code Blocks
  4. Identifiers and Data Types
  5. Data types: Numbers
  6. Input
  7. Modules, Functions, Parameters and Return Values
  8. Conditionals
  9. Loops
  10. Data Types: Strings
  11. Tuples and Lists
  12. Dictionaries
  13. Object-Oriented Python

There are some strong similarities between Java and Python, but some interesting differences as well. Once you've learned one programming language, you have an enormous advantage in learning additional languages: you already know how basic data structures and control structures work. All you have to do is learn a new syntax.

1.1. General Information

1.1.0. Getting Python on your machine

Some computers come with a version of Python pre-installed. In here, however, we're going to be using Python 3, installed via Miniconda. Download the correct package for your computer and install it following the instructions.

1.1.1. Python2 vs. Python3

There are two different versions of Python commonly used now. We'll be using Python3 in here, the "modern" version, but you should know that if you look at some textbooks/websites for examples or help, their Python2 examples may not work for you.

In this bootcamp, we'll point out two significant differences that you'll need to know.

(Note that Python2 comes pre-installed on Apple computers. Cool! But it's Python2, which is the reason you need to install Miniconda.)

1.1.2. Running Python

There are two ways that you can run Python. Interactive mode is perfect for testing small snippets of code to see how they work. Program mode is useful for running programs.

1.1.2.0. Interactive mode

To start the Python interpreter, type python at the command line and hit the [Enter] key:

$ python [Enter]
Python 3.5.1 |Continuum Analytics, Inc.| (default, Dec  7 2015, 11:24:55)
[GCC 4.2.1 (Apple Inc. build 5577)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

The >>> prompt let's you know that Python is waiting for you to enter an instruction.

First Python statements

Try entering the statement here, press the [Enter] key to execute it, and see what happens:

>>> print(“Hello, world!”)

Then try a few other statements in the interpreter to see what happens:

Python 3 vs Python 2: print() statements

In Python 2, the print statement allowed you to print things.

>>> print "Hello, World!"      # only works in Python 2

In Python 3, the print statement is a function that takes a parameter:

>>> print("Hello, World!")      # works in Python 3 or Python 2

Because we're using Python3, make sure to include parentheses with your print statements.

Once you're done with your interactive session, exit by typing "exit()", or holding down the ctrl key while pressing d.

>>> ^D

Executing instructions like this one at a time in the interpreter is kind of cool, especially when you're trying to figure out a command that's new to you, or if you want to experiment real quickly with an idea.

There's one big disadvantage, though: if you want to execute your instructions again, you have to type them in again. Usually, it's much better to write a program.

1.1.3. Writing a Python program

Before Python can compile and run the program, you have to write the program, for which you need a text editor.

Write "Hello, World!" in Python

Do this:

  1. In the Terminal, cd to your Desktop
  2. Launch nano or another text editor to create the program hello_world.py.
    $ nano hello_world.py
  3. This program will just have three lines in it: one line will print out the message "Hello, world!" when it runs, the next line will print the sum of the numbers 1-10, and the last line will print a personalized goodbye message to you. Once you think the program is done, save it, and try running the program.

1.1.4. Running a Python program

As a developer you'll usually use two separate windows, one in which you're working on your program with a text editor or IDE, and one a terminal window that you're using to run your program.

Run the hello_world.py program

Do this:

  1. In a Terminal window, enter
    $ python hello_world.py
    to run the program
  2. If the program doesn't work as you expected, or you get a message saying that the program had an error, it's not big deal. The debugging begins!
  3. Move to the text editor window to try to fix the program, then move back to your test window to try to run the program again.

Show/hide solution

print("Hello, world!")
print(1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10)
print("Goodbye, Richard!")

1.2. Output

We've already seen the print statement that is used to print out values.

>>> print("Hello, world!")
Hello, world!

Let's see some other ways to create output.

1.2.0. Printing multiple values on a single line

The print() statement can be used to print multiple values by separating them with a comma.

>>> print("Hello,","world!")
Hello, world!

Note that the comma inside the quotes got printed, and the comma outside the quotes didn't. Note, also, that Python inserts a space between comma-separated values when it prints them out. You can avoid this by concatenating values with a + sign:

>>> print("Hello" + "world" + "!")
Helloworld!

1.2.1. Printing multiple lines with a single print statement

A single print() statement can print on multiple lines by using the \n (newline) character.

>>> print("Hello,\nWorld!")
Hello,
World!

1.2.2. Using multiple print statements to output a single line

A print() statement typically displays the information indicated and then places cursor on the next line for subsequent print statements. If you want to have multiple print() statements output information on the same line, use the end='' modifier:

print("Hello, ",end='')
print("world!")

Output:
Hello, world!

1.2.3 Print formatting

For specialized output needs—controlling spaces, justifying text, indicating how many decimal places should be printed—you can use Python's .format() method.

Formatting Output

The basic way to format output in Python3 is as follows:

print("{0} is the {1}".format(value0, value1))

Thus:

>>> print("{0} is the {1}".format("Richard", "best"))
Richard is the best

Or:

>>> print("The value of {0} is {1:.4f}".format("pi", math.pi))
The value of pi is 3.1416

What's happening here? A print() statement includes the string to be formatted—with curly braces enclosing the formatting instructions—followed by a call to the .format() method, which includes references to the values that are going to be formatted.

The formatting instructions in the curly braces include a reference to which value is being formatted (0, 1, etc.), and an optional formatting rule (following a colon) that should be followed in displaying that value.

Note that there are a much wider variety of format possibilities available than what is listed here. For example, this snippet produces a very nicely formatted table:

print("{0:>8s} | {1:>8s} | {2:>8s}".format("number","square","root"))
for i in range(10):
    print("{0:8d} | {1:8d} | {2:8.3f}".format(i, i*i, i**0.5))
  
  number |   square |     root
       0 |        0 |    0.000
       1 |        1 |    1.000
       2 |        4 |    1.414
       3 |        9 |    1.732
       4 |       16 |    2.000
       5 |       25 |    2.236
       6 |       36 |    2.449
       7 |       49 |    2.646
       8 |       64 |    2.828
       9 |       81 |    3.000

See the Python3 documentation for further information on how to use print formatting.

1.3. Comments and Code Blocks

1.3.0. Comments

Python supports inline and multiline commenting.

#!/usr/bin/env python3
"""
hello_world.py
The classic introductory "Hello, World" program, Python-style.
"""

print(“Hello, world!”)
print(1+2+3+4+5+6+7+8+9+10)
print("Goodbye, Richard")

1.3.1. Magic Methods (Double-underscore variables, or dunders)

Magic methods are special methods pre-defined in Python and called in certain situations. The double-underscore that precedes and follows the method name is intended to distinguish these special methods from others that you might define yourself.

If you've written your own class, you know that writing the method __init__(self) is required. Python needs to know how it should go about constructing an object for your class type when it is created, and the "dunder-init-dunder" method does that.

You probably haven't used it directly but the __add__ dunder-method is called whenever a plus-sign ( + ) is used in an expression. That method, defined for numbers, will add them numerically. The same method defined for strings will instead concatenate them.

There are many other examples of dunder-methods, and we'll have occasion to use some of them in this course later on.

1.3.2. Code blocks, or Suites

Most programing languages consist of logical sections of code which are grouped together. The body of a function, perhaps, or the body of a loop. In Java, these logical sections of code are enclosed in { }, and the end of an instruction is indicated with a ; (semicolon).

In Python, these logical sections of code are called suites.

Suites, or code blocks

A suite in Python is a logical block of code, identified at the beginning by a colon (:), with each instruction on its own line, and all lines indented exactly 4 spaces from the previous line.

NOTE: Do not use [tab]s to create your spacing unless you have configured your text editor to replace tabs with spaces. Four spaces may look the same as a [tab], but Python won't be able to run your program correctly. Also, although the "4 spaces" rule is not required by Python—you can actually use any spaced-indent for a suite, as long as the same indent is used consistently throughout the program—in here we'll be using 4-spaces, which is standard Python "best practice."

Let's begin right away with setting up a suite that you'll use in practically every program that you write in the class: a main function that comprises the body of your program.

A main() function

The following program uses a main function to organize the instructions:

#!/usr/bin/env python3
"""
Hello, World! program
"""

def main():
    print(“Hello, world!”)
    print(1+2+3+4+5+6+7+8+9+10)
    print("Goodbye, Richard")
  
if __name__ == "__main__":
    main()

Here, the def instruction in Python defines a function called "main", which contains a bit of code that won't be run until the name is used later on in the program.

Down below, then, we "call the function" by writing main(), which tells the interpreter to execute the instructions that we'd described earlier on in the function definition.

1.4. Identifiers and Data Types

Computers need to store data in variables, which are indicated by identifiers.

Definition: Identifier

An identifier is a label used to refer to a specific storage location in the computer's memory.

Examples of identifiers that might store data in a Python program include:

In Python, an identifier may consist of letters, numbers, and underscores ("_"), but it can't begin with a number.

Multi-word variable names are usually written with underscores rather than camelCase—is_not_prime rather than isNotPrime, for example. Constants are written in all capital letters, however, with underscores used to separate words as required. Regular variable names should begin with a lowercase letter (my_triangle, for example), while class names should begin with an uppercase letter (Triangle).

Java is a strongly-typed language in that each variable that you use has to be declared to be of a specific data type (int, double, String, boolean, etc.).

Python is a weakly-typed language in that the type of value stored in a variable doesn't have to be declared in advance, or even at all. If you put an int into the variable n, then n is storing an integer. And there's no rule against then storing a string value in there. You're free to do so.

This freedom comes at a price, however. It means you have to work a little harder to make sure that you don't mess things up...

Data types in Python include:

Python also fully supports objects, which we'll be discussing a little later.

1.5. Data Types: Numbers

1.5.0. Numeric Data Types—Integers and Floating Point numbers

It's far easier for computers to deal with integers, and therefore far faster for them to perform operations with integers. Differences in speed won't be apparent in most programs that we write, but for any program that performs the same operation thousands, million, or billions of times, working with integers where possible produces a significant increase in speed.

Integers

Plain integers are simply that: integers, i.e. numerical values without a decimal point. You can confirm the type of a number by using the type() function in interactive mode:

>>> type(3)
<type 'int'>

Integers have 32 bits of precision. If you need more than that, you'll need to use the longint data type, which has unlimited precision. longint values are indicated with an "L" at the end of the number, i.e. 3L.

Floats

Floats are floating point numbers, i.e. numerical values that do have a decimal point.

>>> type(3.0)
<type 'float'>

Start up the Python interpreter and try out each of the examples here. Can you predict what the output of each will be?

>>> 5 + 2
>>> 5.0 + 2
>>> 5.0 / 2
>>> 5 / 2
>>> 5 // 2  # how is this different?
>>> 5 % 2  # this is related to the last one
>>> 5 * 2
>>> 5 * 2.0
>>> 5 ** 2
>>> 2. ** 0.5

Note that the // operator allows for "whole number" division—it returns the integer result of the division without a decimal and without any remainder.

The % operator gives you that remainder.

These two division strategies turn out to be enormously useful to us.

Python3 vs. Python2

In Python 3, the division operator / works just as you'd expect it would:

print(5 / 2)     # produces result 2.5 in Python 3

In Python 2, the division operator, working on integers, produces an integer result only:

print(5 / 2)      # produces result "2" in Python 2

You can use Python's built-in functions to convert from one data type to another:

>>> print(float(3))
3.0
>>> print(int(3.7))
3
>>> print(round(3.7))
4

1.5.1. The math module

Modules

A module in Python is a program that someone has written that can be used in a larger program. Collections of modules that can be used by programmers are called libraries.

One of the tremendous powers of Python is this ability to leverage the work that other people have already done, and the way one does that in Python is by importing modules.

The import command

To import a module that someone has already written so that you can use it in your own program, simply include the statement import <modulename> at the beginning of your program, just after your opening comments.

One important module is the math module, which includes lots of methods for handling more sophisticated math functions.

Start the Python interpreter, try out the following statements:

Let's see how to use the math module to perform calculations. Enter the following code in the Python interpreter.

>>> import math   # Gets the entire math library
>>> r = 3
>>> circumference = 2 * math.pi * r    # use pi from library
>>> print(circumference)

What if you wish to calculate the square root of a number? There are a couple of ways to do that:

>>> print(11**(0.5))
3.31662479036
>>> print(math.sqrt(11))
3.31662479036

The operation ** indicates an exponent, raising some value to the power indicated.

What do you think this code might print?

>>> print(math.sin(math.radians(90)))

Other functions contained in the math module include:

Python's trig functions require that the value given be in radians. So, to calculate the horizontal component of velocity for a jet traveling 200 meters/second at 30 degrees above the horizontal:

>>> import math
>>> horz_vel = 200 * math.cos(math.radians(30))
>>> print(horz_vel)
173.205080757

Note that if you forget to import the math module, you'll get an error.

For a more complete list of methods available in the math module, use help(math) after importing the module.

1.5.2. Different ways to import a module

Modules obviously provide a convenient way of expanding Python's capabilities beyond the built-in functions that are available any time Python executes a program.

We've already seen the traditional way of importing a module. Note that we have to indicate the module name (math before we indicate the method we want to used from that module (.pi).

>>> import math   # Gets the entire math library
>>> circumference = 2 * math.pi * r    # use pi from library

This technique, at the start, is the recommended way to use a method from an imported module.

Another option, though, if you really only need the pi function, is this:

>>> from math import pi          # Gets just the pi function
>>> circumference = 2 * pi * r   # calculates using pi

The advantage here is that you can just use the identifier pi all by itself, without having to also specify the module name. The disadvantage is that there is some potential for confusion between the pi that you've imported and any other pi variable that you might be using in your program. The likelihood of this happening in a small program is pretty low, but as your programs become more complex, clearly distinguishing between your local variables and those imported from modules will be important.

The previous example gets only the pi function. If you wanted to get all the functions from math, you would do this:

>>> from math import *    # Gets all math functions
>>> print(pi, sin(pi/2))   # use any function directly

1.6. Input

To assign values to a variable, there are two common options: programmer-assigned values and user-assigned values.

1.6.0. Programmer-assigned values in variables

Programmer-assigned value to a variable

To assign a value to a variable in a program:

<variable> = <expression>

Take a look at this code snippet to make sure you understand how it works.

height_in_feet = 5.8
height_in_inches = 12 * height_in_feet
height_in_inches = height_in_inches + 0.5    # she grew!
print("The height in inches is now", height_in_inches)

Note that we're using self-documenting identifiers here, and rather than using CamelCase to identify the words, we're using underscores between words. This is a Python convention that you should follow.

Problem: Area and Circumference of a Circle

Write a program that uses the math module to calculate the area and circumference of a circle with a radius of 2.7 meters.

Expected results:

Area: 22.902210444669592 meters-squared
Circumference: 16.964600329384883 meters

Show/hide solution

#!/usr/bin/env python3
"""
circle_calculator.py
Calculates the area and circumference of a circle with radius 2.7 meters
"""

import math

def main():
radius = 2.7
print("Area:", math.pi * radius * radius, "meters-squared")
print("Circumference:", 2 * math.pi * radius, "meters")

if __name__ == "__main__":
main()

1.6.1. User-assigned values in variables

We wouldn't want to have to rewrite our program for every user that has a different height, however. If we want the user to be able to assign a value at run-time, we can use the input statement.

User-assigned value to a variable

To allow a user to enter a value while the program is running:

<variable> = input(<prompt>)       # for entering strings
<variable> = int(input(<prompt>)) # for entering integer numbers
<variable> = float(input(<prompt>)) # for entering decimal numbers

Example:

guests = int(input("How many are coming to the party? "))
height_in_feet = float(input("Enter the height in feet: "))
height_in_inches = 12 * height_in_feet
print("The height in inches is", height_in_inches)

The input() statement is the other major difference that you need to think about when it comes to Python 3.

Python3 vs. Python2: input statements

In Python 3, the input statement allows the user to enter a str value. If that string, say "34", needs to be evaluated as a number, then the float() function needs to be called, as we've seen.

In Python 2, the input statement is only used for numbers. If you want to have a user enter a string value, the raw_input() function is used.

name = raw_input("Enter your name: ")   # only works in Python 2

Temperature conversion

Write a Python program that has the user enter a temperature in Fahrenheit and then print out the corresponding temperature in Celsius.

Test cases:

Input --> Output
32 -->   0
212 --> 100
98.6 --> 37

Show/hide solution

#!/usr/bin/env python3
"""
tempconverter.py
Converts temperature in Fahrenheit to temperature in Celsius
"""

def main():
    degreesF = float(input("Enter temperature in degrees Fahrenheit: "))
    degreesC = (5 / 9) * (degreesF - 32)
    print("The equivalent temperature is", degreesC, "degrees Celsius.")
  
if __name__ == "__main__":
    main()

1.7. Functions, Parameters, and Return Values

Functions are an important tool for any programmer. They allow us to organize and re-use code, and they have their own scope. Let's see how they work in Python.

1.7.1. Definition and Uses of Functions

Functions

A function is a small program inside a larger one, whose purpose is to:

You should include functions in your program if:

How do you actually do that?

Defining a Function

Just as we've used def main(): to define a main block of code that runs, we can define other functions with blocks of code that will execute when we "call" the function.

def <function_name>(<optional function parameters>):
    <statement>
    <statement>
    <statement>
    <optional return value>

As an example, here's a short function to say "Hello" to somebody:

def say_hello(name):
    print("Hello,", name, "!")

These lines just define the function, but the code doesn't actually execute in our program until we call it:

def main():
    say_hello("Jon Snow")

At this point, the "Hello, Jon Snow !" message is displayed.

The entire program looks like this:

#!/usr/bin/env python3
"""
Simple program demonstrating the definition and use of a function
"""
def say_hello(name):
    print("Hello,", name, "!")

def main():
    say_hello("Jon Snow")
  
if __name__ == "__main__":
    main()

1.7.2. Parameters and Scope

We can write functions that can be used in three different ways:

Let's see how to write each type of function with a few examples.

1.7.2.0. Stand-alone functions

A stand-alone function doesn't need to interact with the main program in any serious way; it doesn't require any information from the main program, and it doesn't need to return any information to the main program. In this case, the function's sole purpose is to keep your code organized so that it's easier to read and manage.

Let's say that you're writing a program that allows the user to play Blackjack on the computer. If you want to print out the instructions for the user, you could certainly put a bunch of print() statements in the main() body of your program.

But it's better to move that block of print statements to someplace else—to a function—and then we'll just call that function if needed.

In most cases, the function definition is placed before the main() defintion. The code, then, looks like this:

def give_instructions():
    advice = input("Need instructions? (y/n)")
    if advice[0].lower() == "y":
        print("""Here's how you play Blackjack. 
        Play begins with 2 cards dealt to each player...
        """)

def main():
    give_instructions()
    .
    .

if __name__ == "__main__":
    main()

When Python executes this code, it:

  1. takes note of the print_instructions function definition and checks it for syntax, but doesn't actually execute that code yet.
  2. takes note of the main() function definition and checks it for syntax, but doesn't actually execute that code yet.
  3. falls through to the bottom of the code to see what it should do. If we're running this program from the command line, then __name__ == "__main__" so the main() function (program) is executed line by line.
  4. encounters the print_instructions() line, causing execution to jump up to that function, and lines there are executed until the function is done.
  5. returns to the line after the function was called in the main() where it continues to execute instructions.

Notice that the main() program now is much less cluttered and easier to read now that I'm using it to call other functions.

1.7.2.1. Sending in information with parameters

In the last example, we didn't need to pass any information back and forth between the main program and the function, but most programs DO need some way of doing that. How do you pass information into the function so that it can actually do something useful? You use parameters.

Parameters

A parameter is information that is passed into a function.

A formal parameter is included in parentheses as part of the function definition. The actual parameter, either a variable or a value, is included in parentheses when the function is called.

Let's take a look at a complete program that actually uses parameters.

"""Calculator Program, by Richard White
This program takes two numbers entered
by the user and performs a mathematical
operation on them.
"""

def add_them(number1, number2):
    print("The sum of",number1,"and",number2)
    print("is",number1+number2)

def main():
    n1 = float(input("Enter a number: "))
    n2 = float(input("Enter a second number: "))
    add_them(n1, n2)

if __name__ == "__main__":
    main()

You can probably guess exactly how this program works. At run time, the main program is called, which asks the user to input two numbers, after which the add_them procedure is called, with those two variables as parameters. In the add_them function, the formal parameters (variables) number1 and number2 refer to the actual parameters n1 and n2. Printing number1 refers to whatever value was referred to when the function was called... and that was n1. The function takes n1 and n2 and prints their sum.

Well, let's try writing a small program to practice using a simple function with parameters.

Problem: Old MacDonald Had a Farm

Write a short program to print the lyrics to the song "Old MacDonald Had a Farm." The program should use a single function "print_lyrics," that takes two parameters—animals and noise—and uses them to print a verse to the song. The main program should then call that function with appropriate parameters ("horses", "neigh", or "ducks" and "quack", etc.) so that the lyrics to the song are printed.

Show/hide solution

#!/usr/bin/env python3
"""
oldmacdonald.py
Prints out lyrics to the song "Old MacDonald Had a Farm" based on animals
and noises
"""

def print_lyrics(animals, noise):
    print("Old MacDonald had a farm, ee-yi-ee-yi-oh")
    print("And on that farm he had some",animals,", ee-yi-ee-yi-oh")
    print("With a",noise, noise, "here, and a", noise, noise, "there")
    print("Here a", noise," there a", noise, ", everywhere a", noise, noise)
    print("Old MacDonald had a farm, ee-yi-ee-yi-oh")
    print()    # blank line to end verse

def main():
    print_lyrics("cows", "moo")
    print_lyrics("ducks", "quack")
    print_lyrics("horse", "neigh")
    print_lyrics("dog", "bark")
  
if __name__ == "__main__":
    main()

Note that there are some minor formatting issues with these lyrics, with some extra spaces inserted into the lyrics at odd places. As an extension, consider using print formatting (see notes above) to clean up the text? :)

1.7.2.2. Scope

In our calculator program above, we used a function to add two numbers and print the result. It turns out that adding two numbers is fine thing to do in a function, but you don't usually want to print anything in the function. Instead, you should get the answer and return it to the calling function which will decide what to do with the answer.

To understand this process, we need to talk about scope.

Scope

Scope refers to the area in a program where a particular variable may be used.

Local scope variables may be used "locally" (in the current function), but don't have any value outside the function.

Global scope variables have value outside a given function, and may be referred to inside a function, but they typically can't be changed by that function.

In our Calculator Program, n1 and n2 are local to main(), and global to add_them. number1 and number2 are local to add_them, and undefined outside of that function.

Exercise: Scope

Examine the following function, and predict what the output of each of the statements will be. (This is not a useful function—it's just designed as an exercise to help you understand scope.)

def hello(a_name):
    print("Hello,",a_name)
    a_name = "Joe"       # Careful! This may not
    print("Hi,",a_name)   # do what you think!
    
    # Now let's see how the function works!
    
def main():
    my_name = "Sue"      # set global variable
    hello(my_name)       # call the function
    
    print(my_name)       # did the function change
                         # the value of myname?
                
    print(a_name)        # will this even work?

Show/hide solution

  1. In the main program, my_name is set to the value Sue
  2. From the main program, the hello function is called with a reference to the string Sue.
  3. From the hello function, a_name refers to the string Sue
  4. The hello function prints out Hello, Sue
  5. The variable a_name is set to point to a different string, Joe. Note that this doesn't change anything in the main() program, where a_name still points to the string Sue.
  6. Still in the function, print(a_name) displays the value of a_name, which is Joe.
  7. The function finishes running, so control returns to the main program, where we print(a_name), producing the output Sue.
  8. The instruction print(a_name) won't work, because the variable a_name doesn't exist in this namespace.
    Traceback (most recent call last):
    File "hello.py", line 13, in 
    print(a_name)
    NameError: name 'a_name' is not defined

It should be clear from looking at even this small example that there's room for some confusion. Different variables have different values in different locations, if they have any value at all. What's the story with all of these parameters?

There are a series of rules that govern what values parameters will have, and under what conditions. The purpose of these rules is to make the code that you write stronger and easier to work with, once you understand the rules. You'll pretty quickly learn to track what value has been given to a function, and how to easily move data in and out of your functions.

1.7.2.3. Getting information back out of a function

We've learned that you can pass information into a function using parameters. But how do you get information back out?

return statements

A return statement in a function that returns operation of a program back to the point where the function was called. Any variables included as part of the return statement are sent back to the function call as a result.

Example: This program takes the two values sent to it, adds them together, and returns the result to the calling function where it can be used in another calculation, printed, etc.

def add_them(a,b):
    return a + b  # Calculate a+b
                  # and sends result back
                  # to function call

def main():
    x = 3
    y = 8
    answer = add_them(x, y)  # Calls function and
                             # puts result in answer
    print("The sum of",x,"and",y,"is",end='')
    print(answer)

Exercise. Roll an n-sided dice roll, but don't print the results

Define a function called dieroller that will simulate rolling a die that has a number of sides specified as a parameter, and returns (doesn't print) the result.

Show/hide solution

def dieroller(n):
    """
    returns a value between 1 and n, inclusive
    assumes that random has already been imported
    """
    return int(math.random() * n + 1)

Being able to return a result to a program rather than just printing out something on screen is the most common way of using functions. If there is any output to be delivered, it will be done so by the calling function.

Let's take a look at our calculator program again. What if we'd written this:

"""
Calculator Program that won't work.
"""

def add_them(number1,number2):
    answer = number1 + number2

def main():
    n1 = float(input("Enter a number: "))
    n2 = float(input("Enter a second number: "))
    add_them(n1, n2)
    print(answer)   # This won't work

if __name__ == "__main__":
    main()

This program won't work as we need it to, because the variable answer is "local to add_them", and undefined in our main program. We need to get the answer back out of the function somehow.

Our programming solution is to return a value.

Here's a version of our program, then, that does work:

"""
Calculator Program that works!
"""

def add_them(number1, number2):
    return number1 + number2

def main():
    n1 = float(input("Enter a number: "))
    n2 = float(input("Enter a second number: "))
    answer = add_them(n1, n2)
    print(answer)

if __name__ == "__main__":
    main()

One common use for functions is to gather user input. Writing separate functions for "initializing" a program (setting initial values for data) and actually processing that data is very common.

Returning multiple values in Python

Often our function will need to return multiple values. In Java, methods can only send back a single value, unless you create, say, a User_Information object to contain many values, and then send one of those back.

Python allows you to return objects back, certainly, but we can also just return multiple values, separated by commas. Take a look at the problem below, and use the solution to see how that would work.

Functions for input

Write a small program that includes a function to get the user's name and age. The function should pass this information back to the main program.

Show/hide solution

def get_input():
    name = input("Enter your name: ")
    age = float(input("Enter your age: "))
    return name, age

def main():
    user, age = get_input()   # Call the function
    print(user, ", you are", age, "years old.")

Now try this one:

Functions for input and calculation

Write a small program that includes two functions: one to get the dimensions of a quadrilateral (and return those dimensions to the main()), and one that uses the information from the first function to calculate the area and perimeter of that quadrilateral and return those to the main(). The main program should then print out the results of the analysis.

Show/hide solution

def get_dimensions():
    length = float(input("Enter length of quadrilateral: "))
    width = float(input("Now enter width:"))
    return length, width

def calc_area_perimeter(length, width):
    area = length * width
    perimeter = 2 * length + 2 * width
    return area, perimeter

def main():
    l, w = get_dimensions()
    a, p = calc_area_perimeter(l,w)
    print("The area and perimeter of your")
    print("quadrilateral are", a,"and", p)

1.8. Conditionals (if, if-else, if-elif-else

The if statement in Python works very much as it does in Java, with some minor differences.

1.8.1. The if statement

The if statement

Python's branching structure is the if statement. In its most basic form, it looks like this:

if <Boolean condition>:
    <statement>
    <statement>
    <statement>
<Statement that is NOT part of the suite>

The Boolean condition has a value that is True or False. If the condition is True, the 3 statements in the indented suite will be executed, and then the statement following that suite will be executed. If the condition is False, the 3 statements are skipped, and only the statement following the suite is executed. (Note that the suite has been indented 4 spaces, the standard in Python.

1.8.1.0 Boolean expressions

Boolean expressions

Boolean expressions evaluate as "True" or "False". One way to create such an expression is simply to assign a variable the value True or False.

Examples:

Another way to create a Boolean expression is to compare two values.

Examples:

Boolean operations include:

==means "is equal to"
!=means "is NOT equal to"
<means "is less than"
>means "is greater than
<=means "is less than or equal to"
>=means "is greater than or equal to"
expr1 and expr2returns True if both expr1 and expr2 are True
expr1 or expr2returns True if either expr1 or expr2 are True
not expr1returns True if expr1 is False, and False if expr1 is True

1.8.1.1. Boolean expressions in branching statements

Let's take a look at how boolean expressions might be used in actual statements.

Predict the result

Examine each of these examples and predict what you think the output will be. Then write each program and confirm your analysis:

user_name = input("Enter your name: ")
if user_name == "Joe":
    print("Hi,", user_name)
age = float(input("Enter your age: ))
if age >= 18:
    print("You're eligible to vote!")
gender = "Female"
if gender != "Female":
    print("No Boys Allowed")
name = "Richard"
if name == "Richard":
    print("I know you!")
    print("You're Richard!")
    print("Thank you!")

1.8.1.2. Additional if statements

There are additional forms of the if statement that often come in handy:

if-else statements

If you want to manage a 2-way condition (either do this, or that), use the if-else statement:

if <Boolean condition>:
    <statement>
    <statement>
else:
    <statement>
    <statement>
<Statement that is continuation of program>

Note that the if and else suites are indented 4 spaces, which helps us visualize that they are the two different options in this part of the program.

Thus, we can correctly analyze whether or not our 21-year-old can be president with the following code:

age = 21
if age < 35:
    print("You're not old enough to be President of the U.S.")
else:
    print("You are old enough to be President of the U.S.")

Only one of the two statements will be executed, depending on the value of the variable age.

Let's try writing a new program.

Try this

A restaurant serves breakfast and lunch until 4pm (1600 hours on a 24-hour time clock), at which time it starts serving dinner. Write a program that has the user enter a 24-hour time, and then indicates whether the restaurant is serving lunch or dinner at that time.

Show/hide solution

#!/usr/bin/env python3
"""
Restaurant lunch or dinner exercise
Richard White, 2012-01-30
"""

def main():
    the_time = float(input("What time is it right now (enter time as hhmm in 24 hour clock): "))
    if (the_time < 1600):
        print("The restaurant is serving lunch right now.")
    else:
        print("The restaurant is serving dinner right now.")

if __name__ == "__main__":
  main()

Some problems require that there be more than a single option considered. For these cases, you basically have two possible ways two write the code:

  1. Organize your logical solution so that a series of if-else statements can be nested, allowing the program's logic to drill down to find the appropriate action, or
  2. if the actions can all be considered simultaneously, use an if-elif-else statement.

Usually one strategy will recommend itself over the other. The next two examples show how each of the strategies can be used to solve the problem of calculating the roots of a quadratic equation.

nested if-else Quadratic Eqn solver

Example of using a nested if-else statement to solve a quadratic equation:

a, b, c, = float(input("Enter coefficients a, b, and c: "))
discrim = b**2 - 4 * a * c  # "**" = exponent
if (discrim < 0):
    print("There are no real roots! Sorry!")
else:
    print("You have roots. Now calculating...")
    if (discrim == 0):   # Here's our nested if
        root = -b / (2 * a)
        print("You have a double root: ",root)
    else:
        root1 = (-b + discrim ** 0.5) / (2 * a)
        root2 = (-b – discrim ** 0.5) / (2 * a)
        print("The roots are:",root1,"and",root2)
print("Program terminating.")

if-elif-else Quadratic Eqn solver

Example of using an if-elif-else statement to solve a quadratic equation:

a, b, c, = float(input("Enter coefficients a, b, and c: "))
discrim = b**2 - 4 * a * c  # "**" = exponent
if discrim < 0:
    print("There are no real roots! Sorry!")
elif discrim == 0:    # "elif" means "else-if"
    root = -b / (2 * a)
    print("You have a double root: ",root)
else:        # if first 2 options passed...
    root1 = (-b + discrim ** 0.5) / (2 * a)
    root2 = (-b – discrim ** 0.5) / (2 * a)
    print("The roots are:",root1,"and",root2)
print("Program terminating.")

Let's try a simple exercise to put some of these pieces together:

Exercise: Restaurant Hours

A restaurant is open for breakfast until 1100 hours, for lunch until 1600 hours, and for dinner until 2300 hours. Write a program that has the user enter the time (in 24-hour form), and then prints out meal the restaurant is serving at that time. Include an additional print statement for each meal that recommends a beverage suitable for that meal. Regardless of the hour, print out the message "Thank you for choosing our restaurant."

Exercise: Letter Grades

Write a main program grade_reporter.py that takes a grade percentage as input (0 - 100), and then sends that information via a parameter to a function called evaluate_grade. That function should return a string value of A, B, C, D, or F, depending on the value of the parameter.

Test cases:

Input --> Output
100        A
99        A
90        A
82        B
75        C
64        D
59        F
105        A
-1        F

1.9. Loops

As in Java, there are two common forms of loops in Python: the while loop and the for loop.

1.9.0. The while loop

The while loop is typically used when you have a loop that needs to repeat some number of times, based on a condition:

Here are some quick examples of common while loops.

1.9.0.0. Counting loops

i = 0
while i < 10:
    print(i)
    i += 1

1.9.0.1. Interactive loops

The interactive loop allows the user to approve a repeated process.

# Program to find the average of numbers
# using an interactive loop
sum = 0.0   # Make sure sum is real
count = 0
keep_going = "Y"
while (keep_going == "Y"):
    num = float(input("Enter a number: "))
    sum = sum + num
    count = count + 1                 
    keep_going = input("Keep going (Y/N)?")
print("You entered",count,"numbers")
print("which have a mean of",sum/count)

This loop works pretty well, but one of the problems is that it's annoying for a user to have to approve every iteration.

1.9.0.2. Sentinel loops

One way of solving the problem of having to have a user repeatedly indicate that he/she wants to continue is by using a sentinel value. This value, when entered with the other numbers, will indicate to the loop that it should stop running.

# Program to find the average of numbers
# using a sentinel loop
sum = 0.0   # Make sure sum is real
count = 0
num = float(input("Enter a number (<0 to quit): "))
while (num >= 0):
    sum = sum + num
    count = count + 1                 
    num = float(input("Enter a number (<0 to quit): "))
print("You entered",count,"numbers")
print("which have a mean of",sum/count)

This loop works much more nicely... but it introduces a new problem: What if one of the numbers that I want to find an average of is negative?

Here's a new trick: have the user enter each number as a string:

# Program to find the average of numbers
# using a sentinel loop
sum = 0.0   # Make sure sum is real
count = 0
num = input("Enter a number, <Enter> quits: ")
while (num != ""):
    sum = sum + float(num)   # eval function converts
                            # a string to a number
    count = count + 1                 
    num = input("Enter a number, <Enter> quits: ")

print("You entered",count,"numbers")
print("which have a mean of",sum/count)

This is the best version of this program yet.

1.9.0.3. Using while loops to validate input

Here are three quick examples of how you can use a while loop to validate a user's input.

# Program to validate input >0
positive_number = float(input("Enter a number >0: "))
while (positive_number <= 0):
    print("Error: please enter a positive number.")
    positive_number = float(input("Enter a number >0: "))
print("Thanks for entering",positive_number,"!")

Here's a slightly different way of doing the same thing, but using an initial "seed" value.

# Program to validate input >0
positive_number = -1   # This is a "pre-seed"
while (positive_number <= 0):
    positive_number = float(input("Enter a number >0: "))
print("Thanks for entering",positive_number,"!")

Poor Programming Style: while loop

This next version of the program uses a loop that would repeat infinitely, except for the fact that—once we get the positive number that we're looking for—we use the break statement to break out of the loop.

# Program to validate input >0
while True:  # This would be an infinite loop, but...
    positive_number = float(input("Enter a number >0: "))
    if positive_number > 0:
        break  # Exits the loop
print("Thanks for entering",positive_number,"!")

This loop operates just fine, but it's considered by most people to demonstrate poor style. Why? For a programmer reading through this code, the instruction while True: is practically useless. The programmer understands that a loop is being entered, but it's unclear what the purpose of that loop is, at least not until later on in the body of the loop. It's far better to use the first versions of these two loops.

1.9.1. The for loop

It's often the case that a loop needs to count through a series of values. A while loop may be written for this purpose, but a for loop is a quick, easy way of accomplishing the same thing.

The for loop

The for loop has the following syntax:

for <variable> in <sequence>:
    <statement>
    <statement>
    <statement>

The <sequence> may be:

Some examples:

for i in range(10):
    print(i)
for i in range(1,10):
    print(i)
for i in (1,10):
    print(i)
for i in range(1,10,2):
    print(i)

Use the explanation of the for loop syntax in the green box above to analyze each of the following loops.

Predict the output

Predict what you think will be outputted by each of these loops. Then enter them into a program or the Python interpreter, and see what happens.

>>> for i in range(10):
...     print(i+1)
>>> for i in range(0,20,3):
...     print(i)
>>> for j in range(10,0,-1):
...     print(j)
>>> for j in (10,0,-1):
...     print(j)
>>> for i in (“Hello, world!”):
...     print(i)
>>> for n in ("Anne","Bob","Coraline"):
...     print(n)

Problem: 99 bottles of beer on the wall

Write a short program to print all the lyrics to the song "99 bottles of beer on the wall." The program should use a single function "print_lyrics," that takes a single integer parameter to print out a verse of the song. The main program should then call that function from inside a loop.

Show/hide solution

"""
99 Bottles of beer on the Wall, by Richard White                             

This program prints out all the lyrics to the song 
using a function called from within a loop.

Future improvements include:
* When only one bottle, don't say 'bottles.'
* At end, instead of 0, say 'No more...'
"""

def print_lyrics(num):
    print(num,"bottles of beer on the wall,")
    print(num,"bottles of beer.")
    print("Take one down and pass it around.")
    print(num - 1,"bottles of beer on the wall.")
    print()

def main():
    for i in range(99,0,-1):
        print_lyrics(i)

if __name__ == "__main__":
    main()

1.10. Data Types: Strings

Java has the char data type, identified by single quotes ('), and the String type, identified with double quotes ("). In Python, there is just the str ("string") class. A string in Python can be identified by enclosing double-quotes ("I'm a string") or enclosing single-quotes ('He said "Help!"').

If you want to print a double-quote mark while you're using double-quotes to indicate a string, but a backslash \ in front of it:

print("So then he said, \"Help!\" ")

# Produces the output
So then he said, "Help!"

1.10.0. Basic Strings and Slicing

The str data type

The str (string) data type is used to work with alphanumeric text.

A string consists of a series of characters, designated as a string by surrounding them with either single quotes (') or double quotes (").

>>> print("Hello, world!")
>>> username = input("Please enter your name: ")

A string is simply a sequence of zero or more characters, and we can easily refer to individual characters by their index, or position in the string.

Slicing

Accessing a string via its indices is called slicing. The indices are referred to by enclosing them in square brackets ([ ]).

Note that the indices for the characters in a string start at 0, not 1. Thus, if password = "secret", password[1] is "e", not "s".

Try these

Predict what will be produced by each of the following statements.

greet = "Hello, world!"
print(greet[1])
print(greet[0], greet[12])
print(greet[7:12])
print(greet[-1])
print(greet[0:4])
print(greet[7:])
print(greet[:5])
print(greet[1:-1])

Show/hide solution

greet = "Hello, world!"
print(greet[1])             # --> e
print(greet[0], greet[12])  # --> H!
print(greet[7:12])          # --> world!
print(greet[-1])            # --> !     (1 character, starting counting from back)
print(greet[0:4])           # --> Hell  (counts up to the position one before 4)
print(greet[7:])            # --> world!   (no value after colon defaults to end of string)
print(greet[:5])            # --> Hello    (no value before color defaults to beginning of string)
print(greet[1:-1])          # --> ello, world   (from character at position 1 to one before last character)

1.10.1. Additional string operations

In addition to slicing, there are a number of string operations that may be useful:

More string functions

Predict what will be produced by each of the following statements.

a_string = "Hello"
b_string = "world"
print(a_string + b_string) # concatenation
print(1 + b_string)        # careful!
print(3 * b_string)        # repetition
print(len(b_string))

Show/hide solution

a_string = "Hello"
b_string = "world"
print(a_string + b_string)      # --> hello_world
print(1 + b_string)             # --> Unsupported operand
print(3 * b_string)             # --> worldworldworld
print(len(b_string))            # --> 5

1.10.2. String Methods

Just as in Java, Python includes the concept of an object. In Python, just about everything is an "object." The objects "Richard", "Fletch", and "Ms. Bush" are all objects of the class string. The function main() is an object of the class function. The values 3, 17, and -204 are all objects of the class integer.

Because we're looking at strings right now, let's take a look at some string methods that you can use to manipulate the string in question.

How to use a Method

You use a method by indicating the object being used, a period (.), and the method name followed by a pair of parentheses. Depending on the method you're using, there may be arguments that you'll need to include in the parentheses.

Example: "Mississippi".count("s") uses the string method "count" to count the number of "s"s in the string "Mississippi" and gives us a value of 4.

Here are some other string methods that you may find useful at some point.

Predict the Output: String methods

Predict what will be produced by each of the following statements, and try them out in the Python interpreter to get some familiarity with them.

name = "jules verne"
print(name.capitalize())
print(name.title())
print("DON'T SHOUT!".lower())
print(name.upper())
print(name.replace("jules","phileas"))
print(name.split())  # splits into a list, default separator is a space " "
print("Wow. That's cool. Right?".split("."))
print(name.find("verne"))
print(name.count("e"))
print(" ".join(["i","love","you"]))     # Joins a list of items into a string separated by " "

Show/hide solution

name = "jules verne"
print(name.capitalize())        # --> Jules verne
print(name.title())             # --> Jules Verne
print("DON'T SHOUT!".lower())   # --> don't shout!
print(name.upper())             # --> JULES VERNE
print(name.replace("jules","phileas"))      # --> phileas verne
print(name.split())  # splits into a list, default separator is a space " "  # --> ['jules', 'verne']
print("Wow. That's cool. Right?".split("."))    #--> ['Wow', " That's cool", ' Right?']
print(name.find("verne"))       # --> 6
print(name.count("e"))          # --> 3
print(" ".join(["i","love","you"]))     #  --> "i love you"

Problem: Censorship

Write a main program that takes a line of text input from user, and then passes that input to a function called censor. That function takes the original string, searches it for the word "hell", and returns a copy of the original string with all instances of the word "hell" replaced by four asterisks ("****").

Problem: Guess the Game

Write a function that takes a string and returns the number of vowels in the string. Then write a main program that asks the user to enter two words. If the number of vowels in the two words is the same, the user loses the game; otherwise, they can keep playing, entering two more words. Can the user guess the rules of the game?

1.11. Data Types: Lists and Tuples

In Java there are a number of types of data that can store a sequence of objects, including Arrays and ArrayLists. In Python, the list type is used.

1.11.1. Intro to lists and tuples

The list data structure

A list is a sequence of data values. Lists may be made up of any type of value: strings, ints, floats, even other lists. Lists are indicated with square brackets enclosing the items in the list, which are separated by commas.

my_data = ['Richard','White','626-845-1235',50]
my_fingers = [1,2,3,4,5,6,7,8,9,10]
friend_list = ["Kathy","Dana","Gary"]

Individual items in a list can be referenced by their index enclosed in square brackets, or by using an "enhanced"-style for loop.

my_phone_number = my_data[2]
for i in range(len(friend_list)):
    print(friend_list[i])
for friend in friend_list:
    print(friend)

Try these

What do you think this code will produce as output?

mylist = ["A","B","C","D","F"]
print(mylist[2])
print(mylist[3:4])  # careful!
print(len(mylist))
print(mylist[:])
print(mylist[::-1])

Show/hide solution

print(mylist[2])        # --> C
print(mylist[3:4])      # --> D
print(len(mylist))      # --> 5
print(mylist[:])        # --> ["A", "B", "C", "D", "F"]
print(mylist[::-1])     # --> ["F", "D", "C", "B", "A"]

You can see that we can use some of the same strategies on lists that we used on strings.

The tuple data structure

The tuple is another type of sequence of data values, very similar to a list. A tuple is indicated by the values, separated by commas, and surrounded by parentheses.

friend_tuple = ("Jill","Brad","Brian")
student_scores = (98, 45, 12, 103, 55)

You can reference the items in a tuple the same exact way that you access it if it were a list of items:

for i in range(len(friend_tuple)):
    print(friend_tuple[i])
for friend in friend_tuple:
    print(friend)

The difference between the list and the tuple is that lists are mutable—we can change the list by adding and removing values or altering the values themselves—while a tuple is immutable. Once it has been created, the series of values can't be changed.

Why you would want to have a series of items that can change when you could just as easily have a series that offers more flexibility is a bit beyond the scope of this class. :)

So, Lists and tuples both store ordered collections of data, referenced in the same way: friend_tuple[1] or student_scores[2]. The difference between a list and a tuple is that a list is mutable (changeable) whereas a tuple is immutable (unchangeable).

So I can add and remove friends from my list of friends:

friend_list.append("Owen")
friend_list.remove("Gary")
friend_list.pop()  # by default, removes the last item in the list
friend_list.pop(0)

If I try to do any of those things with my friend_tuple, I get error messages. The .append(), .remove(), and .pop() methods don't exist for tuples.

1.11.2. Using lists

1.11.2.0. Iterating through a list

How are lists so powerful? Just as we've used a for loop to run through a range of numbers, we can easily set up a loop to run through a series of items in a list.

As in Java, there are two strategies for going through a list. Which way you'll choose to write your list iteration depends on what you need to do.

Looping through a list with an index variable

You could run through the items in the list this way:

CODE:
shopping_list = ["apples", "oranges", "bananas"]
for i in range(len(shopping_list)):
    print("I need to buy",shopping_list[i])

OUTPUT:
I need to buy apples
I need to buy oranges
I need to buy bananas

Here, the index i changes as we go through the list, so each time we refer to shopping_list[i], we get a new value. This is the loop to use if you want to remember the location(s) of specific value(s) in your list as part of the program you're writing.

Looping through the list with an iterator

You can also go through all the items in the list this way, using iteration:

CODE:
for item in shopping_list:
    print("I need to buy",item)

OUTPUT:
I need to buy apples
I need to buy oranges
I need to buy banana

Just as in a for loop with numbers, this loop will repeat: the first time through, item will represent the first piece of data in the list shopping_list ("apples"), the second time through it will be "oranges", and the third time through, "bananas".

The advantage to this loop is that you don't need an index variable like i to refer to each item in the list. The disadvantage is that this loop runs through each item once, from beginning to end. If you need more flexibility in your program, you'll need to use the index-strategy mentioned above to go through the loop.

1.11.2.1. Some list operations

Useful list operations

Some of the more useful operations that can be performed with lists include:

shopping_list = []        # initializes an empty list that can be used to store items
list_A + list_b            # concatenates two lists into one
list_A * <integer>         # repeats a list a number of times
list_A[n]                  # gets the item located at position n
list_A[i:j]	          # slices a subset of list, starting at index i and ending at j-1
len(list_A)                # determines the length of a list
for j in list_A:           # iterate through the list
if (j in list_A):          # boolean, checks to see if an item is in the list

1.11.2.2. Problems

Maximum of 7 values in a list

Write a program that puts the numbers 2, 6, 4, 12, 90, 15, 13 into a list, and then write a loop that goes through the list one by one to find the largest number in the list.

Show/hide solution

Here are a couple of ways to do that. This first way goes through the list using an index variable.

numberList = [2, 6, 4, 12, 90, 15, 13]
position_of_max = 0  # First number is the biggest we've seen
for i in range(1,len(numberList)):
    if numberList[i] > numberList[position_of_max]:
        position_of_max = i
# And now that we've gone through the whole list...
print(numberList[position_of_max])

This second way iterates through the list without an index variable:

numberList = [2, 6, 4, 12, 90, 15, 13]
maxValue = numberList[0]  # First number is the biggest we've seen
for number in numberList:
    if number > maxValue:
        maxValue = number
# And now that we've gone through the whole list...
print(maxValue)

Sometimes it's useful to be able to make a short list of items and then use that list of items to help solve a larger problem.

In this next problem, we're going to use a list of the vowels (not including 'y'): vowels = ['a','e','i','o','u']. We're also going to use a powerful boolean operator in to identify when a letter is in that list:

vowels = ['a','e','i','o','u']
my_letter = "j"
if my_letter in vowels:
    print("We found a vowel!")

Finding vowels

Write a program that asks the user to enter a word, and then tells them how many vowels there are in the word. The program should use a list called vowels which stores the 5 vowels in it. When the program runs through the word entered by the user, it will check each character in the input word to see if that character is in the list of vowels, and increment a counter when one is found. After going through the word, print out the number of vowels found.

Show/hide solution

Here's one solution:

user_word = input('Please enter a word: ')
vowel_count = 0
vowels = ['a','e','i','o','u']
print("The vowels in your word are: ", end='')
for letter in user_word:
    if letter in vowels:
        print(letter, " ", end='')
        vowel_count += 1
print("There were",vowel_count,"vowels.")
print("I'm terribly sorry if I missed any 'y's.")

Can you see how to improve the program to look for the occurrence of y's, but perhaps only count them if no other vowels have been found?

Show/hide modifications

userWord = input('Please enter a word: ')
vowel_count = 0
y_count = 0
print("The vowels in your word are: ", end='')
for letter in user_word:
    if letter in ['a','e','i','o','u']:
        print(letter, " ", end='')
        vowel_count += 1
    elif letter == 'y':
        y_count += 1
if vowel_count == 0:
    if y_count > 0:
        print(y_count * 'y ')    # Prints out y letters
else:
    print("non-existent.")
    print("I don't think you entered a real word.")

Problem: Censorship, part 2

Write a main program that takes a line of text input from user, and then passes that input to a function called censor. That function takes the original string, searches it for any of the bad_words "hell", "darn", "fart", and "dummy," and returns a censored copy of the original string. Each letter of the bad word should be replaced by an asterisk in the censored version (ie. "darn" will be replace by "****" and "dummy" will be replaced by "*****".)

1.11.2.3. List methods

Most programs that involve lists will need to manage them dynamically, adding items, deleting items, or reordering the items.

List methods

List methods that you may find helpful include:

list_A.append(x)       # add an element to end of list_A
list_A.sort()        # sort list_A
list_A.reverse()     # reverse list_A
list_A.index(x)      # find the first location of x in list_A
list_A.insert(i, x)       # insert x into list_A at index i
list_A.count(x)          # count how many occurrences of x in list_A
list_A.remove(x)     # remove the first occurrence of x from list_A
list_A.pop(i)        # returns element i from list_A, and removes it from list
list_A.pop()         # acts on the last item of the list.

Make a list

A common need is to have a program make a list of items. Write a short program that initializes an empty list shopping_list, and then uses a while loop to repeatedly ask the user to enter items that s/he need to pick up at the store. The loop will add each item to shopping_list until the user enters nothing (""). The entire shopping list should then be printed out.

Show/hide solution

print("Enter a list of items to get at the store.")
shopping_list = []
more_items = True
while more_items:
    an_item = input("Enter an item (<Enter> to stop)")
    if an_item != ''
        shopping_list.append(an_item)
    else:
        more_items = False
print("When you go to the store, you need to get: ")
for item in shopping_list:
    print(item)

You may find it useful to convert from strings to lists, or vice versa.

Converting between strings and lists

A list of primes

Write a program that creates a table of the first thousand primes. Recall that a prime number is an integer greater than 1 that is evenly divisible only by itself and 1.

Your prime numbers should be stored in a list called "primes," and your strategy should consist of:

Your program needs to print out the number of the prime, and next to it the value of that prime. Thus, the first few lines of output will be:

Prime #      Prime Value
1            2
2            3
3            5
4            7

1.11.2.4. Nested lists → a list inside a list

Just as we can nest if-else statements inside other if-else statements to work on multiple levels of a problem, we can nest loops inside other loops.

1.11.6.0. Nested counting loops

This demonstrates an odometer effect.

odometer.py

Enter this program and run it to see what effect is produced.

#!/usr/bin/env python3
"""
odometer.py
Demonstrates an odomter using nested loops with print formatting
and clear screen function.
"""

import os           # needed to clear the screen
import time         # needed to slow down the counter

def main():
    for i in range(10):
        for j in range(10):
            for k in range(10):
                print("{0:1d}{1:1d}{2:1d}".format(i,j,k))
                time.sleep(0.1)
                os.system("clear")   # may need to be altered for your system

if __name__ == "__main__":
    main()

1.11.2.5. Nested loops for traversing a grid

One very common type of loop pattern involves using two loops to work through a 2-dimensional grid or table.

What does this nested loop do?

print("  |  0  1  2  3  4  5  6  7  8  9")
print("--+------------------------------")
for row in range(10):
    print(row,"|",end='')
    for col in range(10):
        print("{0:3d}".format(row * col), end='')
    print()

Show/hide solution

This program, after printing out a couple of header lines, has a row loop that runs from 0 to 9. Inside that loop is a second col loop that takes on the values 0 to 9. The print statement uses formatting to print out the product row * col in a space that's 3 characters wide. The effect:

  |  0  1  2  3  4  5  6  7  8  9
--+------------------------------
0 |  0  0  0  0  0  0  0  0  0  0
1 |  0  1  2  3  4  5  6  7  8  9
2 |  0  2  4  6  8 10 12 14 16 18
3 |  0  3  6  9 12 15 18 21 24 27
4 |  0  4  8 12 16 20 24 28 32 36
5 |  0  5 10 15 20 25 30 35 40 45
6 |  0  6 12 18 24 30 36 42 48 54
7 |  0  7 14 21 28 35 42 49 56 63
8 |  0  8 16 24 32 40 48 56 64 72
9 |  0  9 18 27 36 45 54 63 72 81

It's a multiplication table!

Let's try writing another nested loop and using it to print something.

The drawboxes program

Write a program called drawboxes that asks the user to enter a number. The program then uses that number as a parameter in a function called Boxy which prints out a large box composed of that many "square-bracket boxes" printed on the screen.

If the user enters 3, for example, the program will need to call the function Boxy(3), which will then produce the output:

[][][]
[][][]
[][][]

Show/hide solution

Just as above, we need to print a box composed of rows of square brackets, with each row consisting of columns of square brackets.

def Boxy(n):
    for i in range(n):
        for j in range(n):
            print("[]",end = '')
        print() # go down to the next line

Once you've master the basic Boxy function, try this one:

The drawrectangles program

Modify the previous program so that it draws rectangles. The user enters a width and a height in the main program, and a modified version of your Boxy function takes that information and uses it to print an appropriate figure composed of "[]" as before.

One of the challenges of this assignment is getting the length and width of the rectangle to display correctly on the screen.

1.11.3. Working with External Files

Let's get some practice working with a real-life data set: sowpods.txt a 2.7MB text file that lists the legal words that can be used in the game of Scrabble.

Download a copy of that file on your computer, and use it to solve the following problems.

How To: Read a text file into a list

To use this text file, you need to get all of the items in the file into a list in your program.

#!/usr/bin/env python3
"""
Read a text file into a program; put each line of the file into a list.
"""

filename = "sowpods.txt"                # must be in same directory as 
                                        # program, or have correct path
infile = open(filename, "r")            # sets up a file object that we 
                                        # can read lines from
wordlist = infile.readlines()           # brings the lines into a list 
                                        # called 'wordlist'
infile.close()                          # properly close the file

# Each file is read as a line with a \n newline character at the end.
# We need to strip those off, and maybe convert them to lowercase?

for i in range(len(wordlist)):
    wordlist[i] = wordlist[i].lower().rstrip()    # convert to lowercase
                                                  # and strip off the 
                                                  # newline character

# show that we got the list of words
for word in wordlist:
    print(word)

Note that some of these commands can be combined and streamlined by using "Pythonic" strategies, such as this one-line command that reads the same file and stores the words in a list.

wordlist = [line.lower().strip() for line in open("sowpods.txt")]

Identify words that start with 'x'

Write a short program that produces a new list of only the words that begin with the letter 'x'.

Show/hide solution

Assuming that you've already read all the words into a list called wordlist:

x_words = []
for word in wordlist:       # "enhanced" for-loop
    if word[0] == "x":
        x_words.append(word)
print(x_words)

# Could also be written as an indexed for-loop
for i in range(len(wordlist)):
    if wordlist[i][0] == "x":
        x_words.append(wordlist[i])

Identify words that only have vowels in them

Write a short program that produces a new list of the words that consist exclusively of vowels: a, e, i, o, u, and y.

Show/hide solution

Assuming that you've already read all the words into a list called words:

vowels = ('a','e','i','o','u','y')
vowel_words = []
for word in words:
    all_vowels = True
    for i in range(len(word)):
        if word[i] not in vowels:
            all_vowels = False
            break
    if all_vowels:
        vowel_words.append(word)
print(vowel_words)

How To: Write a list to an external text file

You may wish to write the contents of a list to an external text file. This uses a process similar to the one we used to read a file. Note that if the filename we're writing to already exists, it will be overwritten by this new version of the file.

# Write the contents of the x_words list to a file

filename = "x_word_file.txt"        # the name of the external file
outfile = open(filename,"w")        # "w" indicates that we'll be writing
for word in x_words:
    outfile.write(word + "\n")      # Note that we're writing the text and
                                    # putting the newline character back in
outfile.close()                     # properly close the file

1.11.4. List Comprehension

One of the uniquely Python features of working with lists is list comprehension, which is simply a shorthand strategy for creating lists from other lists.

1.12. Dictionaries

One of the most important data types that we haven't yet discussed is the dict, for "dictionary." Dictionaries are used for manipulating unordered key-value pairs of information.

Let's see what that means.

1.12.1. Definition of a dictionary

Definition: Dictionary

A dictionary is a data type in Python that is unordered collection of key-value pairs, what most programming languages refer to as a hash.

An example of a simple dictionary might be this one, which tracks point-values for Scrabble letters:

scores = {"a": 1, "c": 3, "b": 3, "e": 1, "d": 2, "g": 2,
        "f": 4, "i": 1, "h": 4, "k": 5, "j": 8, "m": 3,
        "l": 1, "o": 1, "n": 1, "q": 10, "p": 3, "s": 1,
        "r": 1, "u": 1, "t": 1, "w": 4, "v": 4, "y": 4,
        "x": 8, "z": 10}

Each value before a colon above is called the "key" a unique value that cannot be shared with any other keys. The value after each colon is the "value" associated with that key—values do not have to be unique.

Once the dictionary scores has been created, we can identify the value of any letter as follows:

>>> print(scores["j"])
8
>>>

Note that the dict data type stores its key-value pairs in an unordered format:

>>> print(scores)
{'w': 4, 'i': 1, 'c': 3, 't': 1, 'x': 8, 'j': 8, 'a': 1, 'p': 3, 'f': 4, 'g': 2, 'h': 4, 'v': 4, 'r': 1, 'k': 5, 'l': 1, 'm': 3, 'b': 3, 'z': 10, 'o': 1, 'u': 1, 'e': 1, 'q': 10, 'd': 2, 's': 1, 'y': 4, 'n': 1}
>>>

The only way to reference the "value" is by using its associated, unique key.

>>> word_sum = 0
>>> for letter in word:
...     sum = sum + scores[letter]
... print("The value of the word is", sum)

1.12.2. Dictionary Methods

There are a number of dict methods that can come in handy:

len(d)          # Number of elements in d
d[k]                # Item in d with key k
d[k] = v            # Set item in d with key k to v
del d[k]            # Delete item k from dictionary d
d.clear()           # Remove all items from dictionary d
d.has_key(k)        # Return 1 if d has key k, 0 otherwise
d.items()           # Return a list of (key, value) pairs
d.keys()            # Return a list of keys in d
d.values()          # Return a list of values in d
d.get(k)            # Same as d[k]

1.12.3. Common Use of Dictionaries

A common use of a dictionary is to tabulate freqencies for a series of values.

Trace through this code or try it out on the computer and see how it works.

#!/usr/bin/env python3
"""
letter_frequencies.py
"""

# Get an input string from the user
sentence = input("Enter a sentence: ")

# Split string up into a list of letters
letters = []
for letter in sentence:
    letters.append(letter.lower())

# Put the letters into a dictonary by frequency count
letter_counts = {}
for letter in letters:
    letter_counts[letter] = letter_counts.get(letter, 0) + 1

# Print out the results
for letter in sorted(letter_counts.keys()):
    print("{0:15s}|{1:3d}".format(letter, letter_counts[letter]))

1.13. List Comprehension, External Files

"""
quick_dirty_reader.py

This program demonstrates how you can quickly import a text file
into a Python program where you can split it up into tokens and do
analysis on it.
"""

__author__ = "Richard White"
__version__ = "2022-11-30"


# read all of the lines into memory
with open('data.txt',encoding='utf-8') as infile:
    # use list comprehension
    # rstrip() cleans off the newline character on each line
    lines = [line.rstrip() for line in infile]
    
# Now we can...
# 1. print each line
for line in lines:
    print(line)
    
# 2. or take a line and separate it into commas-separated values
for word in lines[0].split(','):
    print(word)
    
# 3. or maybe they're numbers instead? Go through and make a list
#    of the numbers
nums = []
for word in lines[0].split(','):
    nums.append(int(word))

# 4. Now do something with the numbers
print("Adding up all the numbers!")
total = 0
for i in range(len(nums)):
    total += nums[i]
print(total)

1.14. Object-Oriented Python

Most people first learning Python don't get a chance to dig into object-oriented programming very deeply, if at all.

We're going to fix that, starting now.

1.14.1. Object-Oriented Principles

You've already been exposed to the principles of Object-Oriented Programming: similar types of objects are organized as classes, with each class describing the instance variables that describe an object, and the methods that can be used to interact with the object.

If you understand this description, the following examples will introduce you to the syntax of objects as they are treated in Python.

And if you're a little hazy on object-oriented principles, looking at this code is a great way to re-introduce yourself to the subject.

1.14.2. The Die class

The Die class is a great place to start learning about simple objects. Take a look at the code below and see if you can figure out what's going on.

#!/usr/bin/env python3

"""
die.py
The Die class is used to create Die objects. This class and its accompanying
tester program in the main() below is to give a simple example of 
1. writing a class
2. writing a constructor
3. creating instance variables
4. writing an accessor method
5. writing a mutator method
6. using the class in a program

"""

import random

class Die(object):
    """
    The Die class represents a Die object of an 
    arbitrary number of sides.
    """
    def __init__(self, number_of_sides):
        """This is Python's constructor syntax, which is used when
        an instance of a Die object is created.
        """
        self.number_of_sides = number_of_sides
        self.roll()    # Give the die an initial value
  
    def roll(self):
        """Rolls the die to get a new result, and returns that
        result.
        """
        self.result = random.randrange(self.number_of_sides) + 1
        return self.result
  
    def view_result(self):
        """Allows the client program to look at the current roll result.
        """
        return self.result
       
    def cheat(self):
        """Change the result upward by one, if that won't make it too high.
        """
        if random.randrange(5) > 0:       # 80% chance of increasing value
            cheat_amount = random.randrange(1,3)   # 1-2 inclusive
            if self.result + cheat_amount <= self.number_of_sides:
                self.result += cheat_amount
                
def main():
    d1 = Die(6)
    print(d1.view_result())
    print(d1.roll())
    print(d1.view_result())
    d1.cheat()
    print(d1.view_result())

if __name__ == "__main__":
    main()

1.14.3. The Fraction class

The Fraction class is a great place to start learning about the writing of non-trivial classes.

It's also a great way to learn a little more about Python syntax, and how we can use overriding to make better use of the classes we're writing.

The Fraction class is described in some detail in our book. The instructor will share some detail on how to develop Fraction in class. You may also want to look at this fraction_tester.py that you can use to test the progress of your own class as you write it.

1.13.4. Inheritance

One of the most important aspects of object-oriented programming is the idea of inheritance, in which superclasses—and their accompanying instance variables and methods—can be extended, and leveraged by subclasses which inherit their capabilities.

1.13.4.1. Example of Inheritance

Once you've created a class, it's possible to make sub-classes that inherit the properties of the super-class, but also expand on those properties.

Take a look at this minimal example showcasing the interactions between classes.

Show/hide example

#!/usr/bin/env python3
"""
persons.py
Demonstrates the use of classes, subclasses, arrays of objects, and
the instanceof() function.
"""

__author__ = "Richard White"
__version__ = "2019-02-07"


class Person(object):
    """Describes a person in terms of their name."""
    def __init__(self,name):
        """Used to construct a Person object"""
        self.name = name
    def get_name(self):
        """Returns the name of the Person"""
        return self.name
    def __repr__(self):
        """Returns a representation of the Person that can be printed"""
        return "Person[name=" + self.name + "]"

class Student(Person):
    """Describes a Student subclass that inherits from the Person superclass"""
    def __init__(self, name, gpa):
        """Constructs a Student object. The name parameter *must* be sent to the
        super class using the instruction
            super().__init__(parameters)
        This initializes the inherited instance variables
        """
        super().__init__(name)
        self.gpa = gpa    # initializes the local variables
    def get_gpa(self):
        return self.gpa
    def __repr__(self):
        """Returns a representation of the Student class. Notice how it 
        references the superclass's __repr__ method.
        """
        return "Student[" + super().__repr__() + ",gpa=" + str(self.gpa) + "]"

class Teacher(Person):
    def __init__(self, name, salary):
        super().__init__(name)
        self.salary = salary

    def get_salary(self):
        return self.salary

    def __repr__(self):
        return "Teacher[" + super().__repr__() + ",salary=" + str(self.salary) + "]"

def main():
    p = Person("Joe")
    print(p.getName())
    print(p)
    s = Student("Mary",3.7)
    print(s)
    t = Teacher("Mr. White",50000)
    print(t)
    school_population = []
    school_population.append(p)
    school_population.append(t)
    school_population.append(s)
    print("----------------------")
    for member in school_population:
        print(member.get_name())
        if isinstance(member,Teacher):
            print(member.get_salary())

if __name__ == "__main__":
    main()

1.14.4.2. The Clothing class

Consider a Clothing class which is going to describe an article of clothing. For out initial consideration of this class, we'll define any clothing object in terms of three values (instance variables):

There are plenty of other characteristics that you could use to describe an article of clothing, but let's begin with these in the interest of keeping things manageable.

What kind of methods do you think you would need to interact with a Clothing object?

Think about it for a moment, and then watch this video.

Object-Oriented Programming in Python, part 1

1.14.4.3. The Shirt class

Now that we've figured out what the Clothing class is all about, let's think about a subclass called Shirt that extends the Clothing class, and inherits from it.

Watch this video to get some idea of how you write a subclass in Python.

Object-Oriented Programming in Python, part 2

1.14.4.4. The Wardrobe Project

Now, you're ready for the Wardrobe project.