Python Reference

Introduction

These pages are meant as a quick reference for the python programming language. All code here assumes you are using python version 3. Use the Table of Contents on the left to select your topic of interest.

The python shell or interpreter is very useful for testing or trying simple python statements. Any examples below that have a triple-greater-than prompt (>>>) are showing output using the python shell, like this:

>>> print("hello!")
hello!

You can start the python shell by typing the python3 command in a terminal window (the dollar sign ($) is the terminal prompt, which you don’t type):

$ python3
Python 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

You can exit the python shell with Cntl-d (hold down the Control key, then hit the 'd' key).

Variables

example

>>> x = 3.14159
>>> name = "Harry"
>>> m = 2
>>> cities = ["Philadelphia", "Denver", "Paris"]
>>> print(x)
3.14159
>>> print(x*m)
6.28318
>>> print(cities*m)
['Philadelphia', 'Denver', 'Paris', 'Philadelphia', 'Denver', 'Paris']

explanation/notes

We are declaring variables using the assignment operator (=). Declaring m = 2 means: create a variable called m that refers to the integer value 2 stored in memory. We can then use the variable m throughout the rest of our program.

Variable names are case sensitive.

>>> bird = "Eagle"
>>> print(bird)
Eagle
>>> print(Bird)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'Bird' is not defined

No spaces allowed in variable names.

>>> my name = "Jeff"
  File "<stdin>", line 1
    my name = "Jeff"
          ^
SyntaxError: invalid syntax

Also can’t start with a number.

>>> 5star = "Hilton"
  File "<stdin>", line 1
    5star = "Hilton"
        ^
SyntaxError: invalid syntax

The variable declaration syntax is always <variable> = <value> or <variable> = <expression>.

>>> pi = 3.1415928
>>> radius = 2
>>> area = pi*(radius)**2
>>> print(area)
12.5663712

Switching the order (value = variable) doesn’t work!

>>> 2 = m
  File "<stdin>", line 1
SyntaxError: can't assign to literal

another example

You can use variables on either side of the assignment operator. Just remember that the right side (the value or expression) is always evaluated first, then assigned to the variable on the left side. That allows something like this (increment a variable) to work:

>>> counter = 0
>>> counter = counter + 1
>>> print(counter)
1
>>> counter = counter + 1
>>> print(counter)
2

The second line above says: get the value referred to by counter, add 1 to it to make a new value, then store that new value in memory in the location that the counter variable refers to. So we pull out what is stored there, make a new value, then store the new value back in the same spot.

Data Types

example

>>> age = 54
>>> height = 5.75
>>> name = "Jeff"
>>> happy = True
>>> dogs = ["Max", "Sam", "Lily"]
>>> print(happy)
True
>>> type(happy)
<type 'bool'>
>>> type(height)
<type 'float'>

explanation/notes

The main data types in python are: integer (int), string (str), float, list, and boolean (bool). There are others (e.g., tuples and dictionaries), but these are the main basic types.

A data type is defined by it’s possible values and the operations that can be performed on those values. For integers, the possible values are all integers (0, 1, 2, …​, and the negative integers -1, -2, …​), and the operations are addition, subtraction, multiplication, and so on. The boolean data type has just two values: True and False (must be uppercase).

The data type really matters for some things!

>>> 5/2
2.5
>>> 5//2
2

The first line above uses floating-point math, and probably does what you were expecting. The second line does integer division (i.e., ignores any remainder).

For the string data type, multiplication by integers is allowed/supported, but multiplication by floats is not.

>>> "hello"*3
'hellohellohello'
>>> "hello"*3.5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't multiply sequence by non-int of type 'float'

The same is true for lists:

>>> dogs*2
['Max', 'Sam', 'Lily', 'Max', 'Sam', 'Lily']
>>> dogs*2.5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't multiply sequence by non-int of type 'float'

In python you do not need to declare what type to store in a variable, so you can freely reassign different types to the same variable (not recommended!).

>>> x = 5
>>> print(x)
5
>>> x = "hello"
>>> print(x)
hello
>>> type(x)
<class 'str'>

This is a tradeoff! Python code is usually shorter and cleaner than other languages, because we don’t have to write out what type everything is (variables and functions). However, this can often lead to other problems (e.g., if you expect x to be a float, but it’s a string) or slower code (sometimes the computer can optimize your code for you, if it knows the type of every variable and function).

another example

Sometimes it is useful to check if something is an instance of a certain data type:

>>> isinstance(5,int)
True
>>> isinstance(5,float)
False
>>> isinstance(5,str)
False
>>> isinstance("5",str)
True

See also:

Functions: builtin

example

>>> S = "Swarthmore"
>>> print(S)
Swarthmore
>>> print(len(S))
10
>>> L = list(S)
>>> print(L)
['S', 'w', 'a', 'r', 't', 'h', 'm', 'o', 'r', 'e']
>>> x = int(5.371)
>>> print(x)
5
>>> type(x)
<class 'int'>

explanation/notes

A function is just a block of code with a given name. You use the name followed by parentheses to call the function: print("hello"). Calling the function executes the code block. After the function executes, the program that called the function continues running.

Using functions, either built-in or creating your own, is the primary way to manage code complexity. As you start to write larger programs, you will break large tasks into smaller, manageable functions.

In all of the function calls above, an argument is provided inside the parentheses. In print("hello"), the print() function is called with the string argument "hello". The arguments are just data sent to the function. Functions can have zero or more arguments. Here’s a call to print() with 3 arguments:

>>> name = "Jeff"
>>> print("Hello, ", name, "!")
Hello,  Jeff !

Sometimes, but not always, a function returns something to the program that called the function. In x = int(5.371) the float 5.371 is sent to the int() function, which converts (truncates) it into an integer (5), and returns the result (the 5). This result is then immediately assigned to the variable x.

Here are some useful built-in python functions:

Table 1. Useful Python Built-In Functions
function description example returns

print()

prints arguments to terminal

print("hello")

nothing

int()

converts arg to integer

result = int(3.141)

integer

float()

converts arg to float

result = float("5")

float

str()

converts arg to string

result = str(10)

string

type()

displays data type of arg

type(10)

type object

len()

returns length or sequence

len(S)

integer

input()

gets input from the user

answer=input("name: ")

string

another example

The input() function is used to get input from the user. It will pause the program, waiting for the user to type something and hit Enter. The argument provided becomes the prompt to the user.

>>> age = input("How old are you? ")
How old are you? 53        <-- user types 53 here and hits Enter key
>>> print(age)
53
>>> type(age)
<class 'str'>

Note the input() function always returns a string, no matter what the user enters (characters, digits, etc).

If you know the user is going to enter a valid integer, you can use the int() function to convert the input string to an integer:

>>> age = int(input("How old are you? "))
How old are you? 53
>>> print(age)
53
>>> type(age)
<class 'int'>

But this won’t work if the user enters something that can’t be converted to an integer:

>>> age = int(input("How old are you? "))
How old are you? pony
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'pony'

See later sections (while (indefinite) loops, try/except) for ways to handle invalid input.

Input

This could mean input from the user, or from a file, or some other source, like pulling data from a web site.

example

>>> number = input("Pick a number from 1-10: ")
Pick a number from 1-10: 4
>>> print(number)
4

explanation/notes

The built-in input() function allows the program to prompt the user for input, then wait for a response.

The argument (a string: "Pick a number…​") is displayed to the user. When the user types in a response (the 4) and hits the "Enter" key, the input data is returned to the program. In the above example, the "4" is returned and immediately assigned to the variable number.

The input() function always returns a string, no matter what the user types (letters, digits, punctuation, etc). If you are expecting the user to enters integers or floats, and your program needs integers or floats to work, you will need to convert the user input to the correct data type:

>>> print(type(number))
<class 'str'>
>>> number = int(number)
>>> print(type(number))
<class 'int'>
>>> print(number)
4

See the try/except section for better ways to check for and convert valid user input.

another example

name = input("Name: ")
prompt = "Hi, %s! Year you were born? " % (name)
year = int(input(prompt))

This code asks for the user’s name, then uses it in the next prompt that asks for when the user was born. The second input is automatically converted to an int and assigned to the year variable. Here’s a sample run of the above code, where the user enters "George" and "1732":

Name: George
Hi, George! Year you were born? 1732

See also:

Output

Typically by output we mean either output to the terminal/screen, or output to a file. The print() function is used for output to the terminal (see examples below). For files, the write() method is used — see Files for examples of writing to a file.

example

>>> name = "Julie Ertz"
>>> number = 8
>>> position = "midfielder"
>>> gpg = 0.23
>>> print(name)
Julie Ertz
>>> print(number)
8
>>> print(name, number, position, gpg)
Julie Ertz 8 midfielder 0.23
>>> print("%s (%d) is a %s averaging %.2f goals/game." % (name,number,position,gpg))
Julie Ertz (8) is a midfielder averaging 0.23 goals/game.

explanation/notes

The print() function can print strings, ints, floats, and other data types. When printing multiple types together, separating them by commas is easiest (useful for debugging!), but doesn’t allow formatting. Using String Formatting is best for complex formatted output.

another example

When printing a large block of text, it is often easier to use a triple-quoted string, which you can format exactly as you want it to display over multiple lines:

rules = """
##############################
Scattegories v0.1
-----------------
Pick a letter and a category list.
You then have 30 seconds to write down an entry for each
category that starts with that letter. Good luck!
##############################
"""
print(rules)

See also:

String Formatting

This is a nice way to handle output strings that include variables. By nice I mean it avoids printing variables separated by commas, or concatenated together with the plus (+) operator.

example

>>> name = "Ryan Howard"
>>> homeruns = 58
>>> average = 0.313
>>> year = 2016
>>> print("In %d, %s hit %.3f with %d home runs!" % (year, name, average, homeruns))
In 2016, Ryan Howard hit 0.313 with 58 home runs!

explanation/notes

You can even create strings with this and use them as needed:

>>> output = "In %d, %s hit %.3f with %d home runs!" % (year, name, average, homeruns)
>>> type(output)
<class 'str'>
>>> print(output)
In 2016, Ryan Howard hit 0.313 with 58 home runs!

In both of these examples, a sting is created by substituting the variables at the end in for the format specifiers (the %s, %d, and the %f). The variables must be in the correct order, so the first variable (year) is substituted for the first format specified (%d).

And here’s what the format specifiers mean:

Table 2. String Format Specifiers
format specifier description example

%d

for decimal integers

print("%d stars" % 3)

%f

for floats

print("pi is %f" % 3.14159)

%s

for strings

print("Hi, %s" % "Jeff")

You can also include width and precision information in the format specifier. For example, the %.3f in the above examples means we want only 3 digits after the decimal in the float.

If I were printing out numbers that I want to all line up (maybe dollar amounts), I could specify the width of each output string. For example:

>>> nums = [21.5, 100, 3.45, 367.39]
>>> for num in nums:
...    print("$%7.2f" % num)
...
$  21.50
$ 100.00
$   3.45
$ 367.39

The %7.2f means I want all floats printed with a width of 7 and with 2 digits after the decimal. Notice that after the dollar sign, all floats have a width of 7 characters (the decimal is one of the 7 characters). If the float doesn’t already have a width of 7, it is padded with spaces on the left side as needed.

another example

There’s also a newer .format way to handle string formatting. Here’s an example of the above using the newer syntax:

>>> output = "In {:d}, {:s} hit {:.3f} with {:d} home runs!".format(year, name, average, homeruns)
>>> print(output)
In 2016, Ryan Howard hit 0.313 with 58 home runs!

See also:

for loops

example

>>> names = ["Andy", "Kevin", "Lauri", "Jeff"]
>>> for name in names:
...    print("Hello, ", name, "!")
...
Hello,  Andy !
Hello,  Kevin !
Hello,  Lauri !
Hello,  Jeff !

Note: I don’t type the dot dot dots (…​). That’s just the python shell telling me it expects something more — the indented code block of the for loop. On the last line, before the "Hello…​" output, I just hit the Enter key (no indentation).

>>> limit = 10
>>> for i in range(limit):
...     output = 2*i
...     print(i,":",output)
...
0 : 0
1 : 2
2 : 4
3 : 6
4 : 8
5 : 10
6 : 12
7 : 14
8 : 16
9 : 18

explanation/notes

for loops are great when we want to repeat something, and we know how many times we want to repeat. In the first example above, we want to print a greeting for however many names are in our list of names. In the second example, we want to create and print something limit times.

The for loop syntax is:

for <variable> in <sequence>:
   indented code
   indented code
   indented code

The variable is any name you want (but choose good variable names, unlike this next example!):

>>> for n in names:
...    print("Hello, ", n, "!")
...
Hello,  Andy !
Hello,  Kevin !
Hello,  Lauri !
Hello,  Jeff !

And the variable takes on all values in the sequence, one at a time. Each time the variable gets a new value from the sequence, the indented code lines (i.e., the code block) are executed.

There must be one (or more) indented code lines in the for loop. The next unindented line ends the code block! So if I had this:

limit = 10
for i in range(limit):
    output = 2*i
    print(i,":",output)
print("-"*20)

It would print this (see how the dashed line is after or outside the for loop!):

0 : 0
1 : 2
2 : 4
3 : 6
4 : 8
5 : 10
6 : 12
7 : 14
8 : 16
9 : 18
--------------------

The sequence in a for loop can be many things: a list, a string, or even a file. If the sequence is:

  • a list, the variable becomes each item in the list

  • a string, the variable becomes each character in the string

  • a file, the variable becomes each line in the file

Here’s a string sequence example:

>>> for ch in "ABCDEFG":
...    print(ch*3)
...
AAA
BBB
CCC
DDD
EEE
FFF
GGG

These examples may all seem trivial or a little silly, but for loops are very important! As we learn more python, we’ll see some more useful examples.

the range() function

The range() function is a built-in python function that is super-useful for creating sequences used in for loops. Technically (in python 3) it is it’s own type, but you can see what it’s doing if you call the list() function on it:

>>> x = range(10)
>>> type(x)
<class 'range'>
>>> list(x)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Given a single integer (say, n) it creates a range of integers from 0 to n-1.

The full syntax is range(start, stop, step), but you can leave out the start (defaults to 0) and/or the step (defaults to 1). Here’s an example using all of them:

>>> list(range(1,11,2))
[1, 3, 5, 7, 9]
>>> list(range(10,-1,-1))
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

another example

>>> n = 7
>>> for i in range(1,11):
...    answer = i*n
...    print(n,"x",i,"=",answer)
...
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56
7 x 9 = 63
7 x 10 = 70

See also:

Accumulator

Suppose you have a list of data values, and you need to know how many of those data values are less than zero.

example

data = [37.5, -0.01, 99.34, 14.7, ...]
counter = 0
for item in data:
   if item < 0:
      counter = counter + 1
print("Number a values < 0: ", counter)

explanation/notes

The counter variable is an example of an accumulator. It starts at zero, and we check each data value in the list. Each time we find one that meets a certain condition (here, less than zero), we increment the counter variable. At the end of the for loop we have built-up/accumulated the answer (how many values are less than zero).

This accumulator pattern (initialize to zero, build up solution one item at a time) arises in different situations, and can be reused to solve problems in these different areas.

Here are some examples:

  • keep track of consecutive correct answers, print a message if user gets 5-in-a-row correct ("Good work!")

  • get data from file, keep a count of how many invalid data items read in

  • get numbers from user, keep a running sum of the input numbers

another example

Here’s the "keep a running sum" example mentioned above. In this example we are getting numbers from the user, then calculating and displaying the average of those numbers.

ngrades = int(input("Number of grades: "))

total = 0
for n in range(ngrades):
    grade = float(input("grade: "))
    total = total + grade

avegr = total/ngrades
print("Average grade: ", avegr)

string accumulator

You can also build-up/accumulate strings, starting with an empty string. What do you think this for loop will print to the screen?

output = ""
for i in range(10):
   output += "*"
   print(output)

Note: the += operator is shorthand for assignment addition. So the output += "*" line above is equivalent to this:

output = output + "*"

if/else (branching)

The if/elif/else statement is used for branching: when your code needs to decide between one or more actions.

example

if height > 48:
   print("Good to go!")
elif height >= 42:
   print("You need an adult with you to ride this rollercoaster!")
else:
   print("Sorry, you are not tall enough for this rollercoaster!")

explanation/notes

The above code prints a different message, depending on the value stored in the height variable. There are three branches in this if statement:

  • people over 48 (inches) can ride on their own

  • people in the 42-48 inch range can ride with an adult

  • people less than 42 inches (i.e., everyone else) cannot ride this rollercoaster

Each branch is a code block. Just like the for loop, the code block is indented, and can be one or more lines long.

The syntax of the if/elif/else statement is:

if <boolean expression>:
   do this line
   and any others in this block
elif <boolean expression>:
   do this other code block
else:
   do this indented line
   and this one, too

The boolean expression can be anything that evaluates to either True or False.

If one of the boolean expressions is True, that code block is executed and no further branches are tried (i.e., the program jumps down to the code that comes after the if/elif/else statement and carries on from there).

You can have a single if statement all by itself:

if grade > 90:
   print("Good work!")

You can also have just two branches (no elif):

if grade > 70:
   print("You passed!")
else:
   print("Uh oh...")

You can have three branches as shown above, or as many branches as you need:

if grade >= 90:
  print("A")
elif grade >= 80:
  print("B")
elif grade >= 70:
  print("C")
elif grade >= 60:
  print("D")
else:
  print("F")

In the last example, if grade has the value 85, the first boolean expression (grade >= 90) is False, so the computer goes on to test the next expression (grade >= 80). Since this one is True, it prints the B and then skips all further tests and would carry on with any code located below/after the if/elif/else statement.

boolean expressions

Here are the boolean comparison operators:

  • equals: ==

  • not equals: !=

  • greater than: >

  • less than: <

  • greater than or equal to: >=

  • less than or equal to: <=

Python allows string comparisons (see ord and chr for how):

>>> "apple" < "banana"
True
>>> "Zebra" > "Tiger"
True
>>> "Jeff" == "jeff"
False
>>> "Apple" < "apple"
True

There is also a membership operator (in) that is really useful:

>>> S = "Swarthmore"
>>> L = ["dog", "cat", "zebra"]
>>> "a" in S
True
>>> "z" in S
False
>>> "dog" in L
True
>>> "camel" in L
False
>>> "more" in S
True
>>> "a" in L
False

another example

You can also combine boolean expressions with and, or, and not:

if (flavor == "chocolate") and (num_scoops > 0):
   print("Yum!!")

For and: both conditions must be True to execute the code block.
For or: the code block will be executed if either or both are True:

if (flavor == "chocolate") or (flavor == "mint chip"):
   print("I'm OK with that. Thanks!")

Functions: simple

Functions are just blocks of code with a name, which we can call using the name and parentheses:

example

>>> def celsius(tempF):
...    tempC = (tempF - 32) * (5/9)
...    return tempC
...
>>> print(celsius(72))
22.22222222222222
>>> print(celsius(32))
0.0
>>> print(celsius(-40))
-40.0

explanation/notes

In the above example we define the function called celsius. It has one parameter: a varible named tempF, which presumably holds the temperature in degrees Fahrenheit that we want to convert to degrees Celsius. Given tempF, we calculate the temperature in degrees Celsius and store it in a variable called tempC. Finally, the function returns the data stored in tempC back to whoever called this function.

The next three lines above just show three examples of calling the function. In the first example (celsius(72)) the argument in the function call is the integer 72. This means, in the function, tempF is assigned the value of 72. In the second call, the argument is 32, and the function calculates and returns 32F converted to Celsius.

As you can see, functions are extremely useful if we ever have to perform repeated calculations. They can also make our programs more readable, if we choose good function names. And they greatly reduce complexity for larger programs, making them both easier to read and to design/write.

The syntax of defining a function is:

def <function>(<params>):
   <body of function>

The name of the function can be almost anything (no spaces, can’t start with a number, similar to variable names). A function can have zero or more parameters. The body of the function can be one or more lines (every indented line is part of the function). A function may or may not return anything to the calling program.

Here’s an example of a function that just prints stuff and doesn’t return anything back to the program that called the function:

def poem(color1, color2):
   """print a silly poem"""
   print("Roses are %s," % color1)
   print("Violets are %s," % color2)
   print("Sugar is sweet.")
   print("And we love CompSci!!")

Calling the above function as poem("red", "blue") produces this:

Roses are red,
Violets are blue,
Sugar is sweet.
And we love CompSci!!

And calling the function as poem("black", "pink") produces this:

Roses are black,
Violets are pink,
Sugar is sweet.
And we love CompSci!!

another example

def average(mylist):
   """calculate and return average of numbers in a list"""
   total = 0.0
   for num in mylist:
     total = total + num
   ave = total/len(mylist)
   return ave

This function calculates the average of all numbers in the given list, as it says in the triple-quoted comment (the first line under the def statement). All functions should have a comment under the def statement, as it helps the reader to understand what the function does, and is also part of the built-in python help documentation.


See also:

def main()

example

def main():
   name = input("What is your name? ")
   print("Hello, %s!" % (name))

main()

explanation/notes

Above we define a short function called main, and then call the function on the very last line. Remember, for functions, anything indented is part of the function, so the first unindented line (in this case, the call to main()) marks the end of the function definition above.

Most programs have a main function that drives the whole program. Reading through main() should give the reader a good overview of what the whole program does (like an outline). And just like other functions, if main() is more than one screen, it’s probably too long (and should be broken up into more functions).

A common practice is to put def main() as the first function defined in the file, with others below. This makes it easy to see/find when you open the program for editing.

another example

def main():
   printRules()
   score = 0
   GAMEOVER = False
   while not GAMEOVER:
      displayBoard(score)
      score = Turn(score)
      if score < 0:
         GAMEOVER = True
   print("Thanks for playing!!")

As you can probably guess, the above is a main() function from a simple game. Another function (printRules()) is called to display the rules of the game, then the board is displayed and the user takes a turn. This repeats (display, turn, display, turn, …​) until the game is over.


See also:

Strings

A string is a sequence of characters between single or double quotes. The characters can be letters, digits, punctuation, spaces, etc.

example

>>> S = "hello"
>>> len(S)
5
>>> print(S[0])
h
>>> print(S[4])
o

explanation/notes

Each character in a string has a position, also called an index. In python, positions start at 0, so the "h" in the above string is at position 0, and the "o" is at position 4 (note: one less than the length of the string).

You can access individual characters in a string using square-bracket indexing:

>>> print(S[2])
l
>>> print(S[3])
l
>>> print(S[4])
o

Any sequence in python can be used in a for loop. For strings, we can either loop over characters in the string or indices (0 to len(S)-1). Here are a few examples:

>>> S = "hello"
>>> for ch in S:
...   print(ch)
...
h
e
l
l
o
>>> for i in range(len(S)):
...   print(i,S[i])
...
0 h
1 e
2 l
3 l
4 o

Strings are immutable, which means you cannot change individual characters in a string. For example, this doesn’t work:

>>> name = "jeff"
>>> name[0] = "J"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

However, you can create new strings from parts of existing strings. Slicing (using [:] to pick out part of the original string) and concatenation (using the + operator) are useful for doing this:

>>> name = "jeff"
>>> name = "J" + name[1:]
>>> print(name)
Jeff

Here are some more slicing examples:

>>> S = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
>>> first10 = S[:10]
>>> print(first10)
ABCDEFGHIJ
>>> last10 = S[len(S)-10:]
>>> print(last10)
QRSTUVWXYZ
>>> middle = S[10:20]
>>> print(middle)
KLMNOPQRST

string concatenation

We can use the accumulator pattern to grow or accumulate a string:

>>> S = ""
>>> for i in range(5):
...    S = S + str(i)
...    print(S)
...
0
01
012
0123
01234

In the above code, str(i) converts the current value of i to a string. This is then added to the current value of S, and assigned back to the variable S.

comparing strings

Strings are encoded using the ASCII encoding scheme. This just means a numeric value is assigned to each possible character (+,-./0123…​.ABCD…​XYZ…​abcd…​xyz…​). When you compare strings, they are compared by these numeric values. It is not important to know the ASCII values, but you should remember that the ASCII values for 'a' to 'z', 'A' to 'Z' and '0' to '9' are contiguous. This means comparing two lowercase strings works as you would expect:

>>> "apple" < "banana"
True
>>> "zebra" > "pony"
True

and comparing two uppercase strings also works:

>>> "Rich" > "Jeff"
True
>>> "Jeff" > "Sara"
False

You can also test if a substring is present in another string using the in (membership) operator:

>>> "a" in "computer science"
False
>>> "the" in "well hello there"
True
>>> "LL" in "hello"
False

ord() and chr()

If you are curious, you can convert single characters to their ASCII values using the ord() function (and convert back using chr()). The ASCII values alone are not that useful, but comparing them and doing math with them can often be useful.

>>> ch = "C"
>>> ord(ch)
67
>>> difference = ord(ch) - ord("A")
>>> print(difference)
2
>>> ord(ch) + difference
69
>>> chr(ord(ch) + difference)
'E'

common string methods

Strings are objects in Python, and thus have methods that we can invoke on them. There are a lot of methods in the str library for creating new string objects from existing string objects and for testing properties of strings. Keep in mind that strings are immutable!

Here are a few str methods that may be particularly useful (run help(str) in the python interpreter to see the full set):

Table 3. String Methods
str method result

upper()

return copy of str converted to uppercase

lower()

return copy of str converted to lowercase

isalpha()

return True if string is all alphabetic characters

isdigit()

return True if string is all digits

count(sub)

return number of occurrences of sub

index(sub)

return index of first occurrence of sub

strip()

strip off leading and trailing whitespace

split()

split into list of "words" (see below)

>>> S = "we LOVE cs"
>>> S.upper()
'WE LOVE CS'
>>> S.lower()
'we love cs'
>>> S.isalpha()
False
>>> S.isdigit()
False
>>> S.count(" ")
2
>>> S.index("L")
3
>>> S.split()
['we', 'LOVE', 'cs']
>>> S = "   we love cs    "
>>> len(S)
17
>>> S = S.strip()
>>> len(S)
10
>>> print(S)
we love cs

converting between strings and lists

Often, in programs that manipulate strings, you want to convert from a string to a list or from a list to a string (due to lists being mutable and strings being immutable).

To convert any string to a list of individual characters, use the list() function:

>>> S = "ABCDEFG"
>>> L = list(S)
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G']

Use the split() string method to split a string into substrings. The substrings are returned as a list. If no argument is given, it will split on whitespace. Using an argument, we can split on any substring:

>>> S = "we love cs"
# split on spaces
>>> L = S.split()
>>> print(L)
['we', 'love', 'cs']
>>> S = "jeff:rich:sara:david"
# split on colons
>>> L = S.split(":")
>>> print(L)
['jeff', 'rich', 'sara', 'david']

Use the join() string method to convert a list back to a string. Here’s a quick example:

>>> print(L)
['jeff', 'rich', 'sara', 'david']
>>> S = "".join(L)
>>> print(S)
jeffrichsaradavid
>>> S = "XXXX".join(L)
>>> print(S)
jeffXXXXrichXXXXsaraXXXXdavid

Whatever string is used as the object ("" or "XXXX"), it is inserted between each item in the list when making the new string.


See also:

Lists

example

>>> names = ["Kevin", "Andy", "Lauri", "Jeff"]
>>> for name in names:
...    print("Hello, %s!" % (name))
...
Hello, Kevin!
Hello, Andy!
Hello, Lauri!
Hello, Jeff!

explanation/notes

A list is a sequence of items, where each item could be anything (an integer, a float, a string, etc), even another list!

Like strings, lists have a length: the number of items in the list.

>>> L = ["pony","donkey","zebra"]
>>> print(len(L))
3

Each item in a list has a position, also called an index. In python, positions start at 0, so the string "pony" in the above list is at position 0, and "zebra" is at position 2.

You can access individual items in a list using square-bracket indexing:

>>> print(L[2])
zebra
>>> print(L[1])
donkey

Any sequence in python can be used in a for loop. For lists, we can either loop over items in the list (see names list above) or indices. Here is an example using range() to loop over indicies:

>>> L = ["pony","donkey","zebra"]
>>> for i in range(len(L)):
...   print(i,L[i])
...
0 pony
1 donkey
2 zebra

Unlike strings, lists are mutable . This means their contents can be modified, without having to create a new list.

>>> L = ["pony","donkey","zebra"]
>>> L[2] = "horse"       # assign to position 2 in the list
>>> print(L)
["pony","donkey","horse"]

The in (membership) operator also works for lists:

>>> positives = ["y","Y","yes","Yes","YES"]
>>> "no" in positives
False
>>> "yes" in positives
True

common list methods

Lists are objects in Python, and thus have methods that we can invoke on them. Here are a few that may be particularly useful (run help(list) in the python interpreter to see the full set):

Table 4. Useful Python list Methods
list method result

append(item)

add item to end of list

insert(index,item)

insert item at index

extend(L1)

add list L1 to original list

sort()

sort the list

reverse()

reverse the list

count(item)

return number of occurrences of item in list

index(item)

return index of first occurrence of item

pop(index)

remove and return item at index

>>> L = list("ABCDEFG")
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G']
>>> L.append("X")
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'X']
>>> L.extend(["Y","Z"])
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'X', 'Y', 'Z']
>>> L.reverse()
>>> print(L)
['Z', 'Y', 'X', 'G', 'F', 'E', 'D', 'C', 'B', 'A']
>>> L.sort()
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'X', 'Y', 'Z']
>>> L.count("E")
1
>>> L.index("E")
4
>>> L.pop(4)
'E'
>>> print(L)
['A', 'B', 'C', 'D', 'F', 'G', 'X', 'Y', 'Z']
>>> L.insert(1,"hello")
>>> print(L)
['A', 'hello', 'B', 'C', 'D', 'F', 'G', 'X', 'Y', 'Z']

converting between strings and lists

Often, in programs that manipulate strings, you want to convert from a string to a list or from a list to a string (due to lists being mutable and strings being immutable).

To convert any string to a list of individual characters, use the list() function:

>>> S = "ABCDEFG"
>>> L = list(S)
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G']

Use the split() string method to split a string into substrings. The substrings are returned as a list. If no argument is given, it will split on whitespace. Using an argument, we can split on any substring:

>>> S = "we love cs"
# split on spaces
>>> L = S.split()
>>> print(L)
['we', 'love', 'cs']
>>> S = "jeff:rich:sara:david"
# split on colons
>>> L = S.split(":")
>>> print(L)
['jeff', 'rich', 'sara', 'david']

Use the join() string method to convert a list back to a string. Here’s a quick example:

>>> print(L)
['jeff', 'rich', 'sara', 'david']
>>> S = "".join(L)
>>> print(S)
jeffrichsaradavid
>>> S = "XXXX".join(L)
>>> print(S)
jeffXXXXrichXXXXsaraXXXXdavid

Whatever string is used as the object ("" or "XXXX"), it is inserted between each item in the list when making the new string.

another example

Here’s a list-of-lists example:

>>> LOL = [["jeff", 77], ["lila", 99.0], ["joshua", 87.3]]
>>> for item in LOL:
...    print("%s's final grade is %.1f" % (item[0], item[1]))
...
jeff's final grade is 77.0
lila's final grade is 99.0
joshua's final grade is 87.3

See also:

Slicing

Slicing is useful for pulling out parts of strings or lists. For example, if you just want the first 6 characters of a string, or the last two items in a list.

example

>>> first = "luke"
>>> last = "skywalker"
>>> first[0] + last[0:6] + "1"
'lskywal1'
>>>
>>> L = list("ABCDEFG")
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G']
>>> print(L[2:5])
['C', 'D', 'E']
>>> print(L[5:])
['F', 'G']
>>> print(L[:2])
['A', 'B']

explanation/notes

Using square-bracket indexing and the slicing operator (":"), the last[0:6] statement grabs just the first six characters (0 through 5) from the last string.

The slicing syntax is [start:stop], meaning grab all items or characters from index start up to but not including index stop.

In the list example, L[2:5] grabs items from the list from positions 2 through 4 (does not include item at position 5).

Leaving off the start (e.g., L[:2]) defaults to the beginning of the sequence. Leaving off the stop (L[5:]) defaults to the end of the sequence.

It’s also OK if the stop is past the end of the sequence:

>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G']
>>> L[5:20]
['F', 'G']
>>>
>>> S = "hello"
>>> S[3:20]
'lo'

another example

>>> strand = "AUGGCCUGGUAA"
>>> for i in range(len(strand)):
...    print(i,strand[i:i+3])
...
0 AUG
1 UGG
2 GGC
3 GCC
4 CCU
5 CUG
6 UGG
7 GGU
8 GUA
9 UAA
10 AA
11 A

See also:

Objects and Methods

This section is just the basics of objects and methods: what they are, and how we use them. See Classes if you are looking for help on creating your own objects.

example

>>> course = "Introduction to Computer Science"
>>> course.count("o")
4
>>> course.count("z")
0
>>> course.isalpha()
False
>>> course.upper()
'INTRODUCTION TO COMPUTER SCIENCE'
>>> grades = [88,90,78,99,93]
>>> grades.sort()
>>> print(grades)
[78, 88, 90, 93, 99]
>>> grades.append(75)
>>> print(grades)
[78, 88, 90, 93, 99, 75]

explanation/notes

Both python strings and python lists are examples of objects. An object is just some data plus some methods (similar to functions) wrapped up together into one thing.

The string methods used above are:

  • count() — returns the number of occurrences of the given substring

  • isalpha() — returns True if all characters in the string are letters

  • upper() — returns an uppercase version of the string

There are many other str methods. In string objects, the data are the characters in the string.

The list methods used above are:

  • sort() — sorts the list in place

  • append() — adds the given argument to the end of the list

In list objects, the data are the items in the list.

object.method() syntax

Methods are always called with the dot syntax: object.method()

Again, methods are similar to functions, except they are tied to the type of object, and are called with this special syntax.

another example

>>> from graphics import *
>>> gw = GraphWin()
>>> p = Point(10,20)
>>> c = Circle(p, 50)
>>> c.draw(gw)
>>> c.move(150,0)

This example uses the Zelle Graphics Library.

Three objects are created: a graphics window, a point, and a circle. The circle is centered at the point’s location (x=10, y=20), and has a radius of 50. The next line calls the Circle’s draw() method to draw the circle in the graphics window. The last line moves the drawn circle 150 pixels in the x direction.


See also:

import statements

Python libraries contain commonly-used functions. In order to use the functions, we need to first import the correct library.

example

Here’s an example of importing the python math library, to gain access to the sqrt() function:

from math import *

for i in range(1,6):
    result = sqrt(i)
    print("Sqrt(%d) = %.2f" % (i, result))

Running the above code would produce this output:

Sqrt(1) = 1.00
Sqrt(2) = 1.41
Sqrt(3) = 1.73
Sqrt(4) = 2.00
Sqrt(5) = 2.24

explanation/notes

In python there are two common ways to import libraries:

from math import *              import math
result = sqrt(5)                result = math.sqrt(5)

The example on the left is typically used in our intro courses, as it requires less typing and makes short codes easier to read.

For larger, more complex codes, the example on the right is preferred. Calling the function as math.sqrt() avoids any possibility of two functions from different libraries having the same name (i.e., if they did have the same name, calling them as library.function() would allow us to know which library each was from).

Some useful python libraries include:

Table 5. Python Libraries
library description functions

math

basic mathematical functions

sqrt(), sin(), cos(), radians(), log2()

random

random number functions

randrange(), choice(), random()

time

manipulate time values

sleep(), time()

To see documentation on any library, simply import the library and then call the help() function:

>>> import math
>>> help(math)

Use 'q' to quit the help documentation.

Usually all import statements are at the top of the file, before any code or function definitions:

"""
Comments...
"""

from math import *
from random import *

def main():
    main code here...
    main code here...
    main code here...

main()

another example

>>> from random import *
>>> L = list("ABCDEFG")
>>> choice(L)
'E'
>>> choice(L)
'A'
>>> choice(L)
'F'

You can also just import one function if you know that’s all you need:

>>> from math import sqrt
>>> result = sqrt(5)

See also:

random library

The python random library provides pseudo-random numbers for your programs (useful for games, etc). There are also functions to choose a random item from a list or sequence.

example

>>> from random import *
>>> randrange(1,101)
60
>>> randrange(1,101)
24
>>> randrange(1,101)
35
>>> randrange(1,101)
91
>>> L = list("ABCDEFG")
>>> choice(L)
'C'
>>> choice(L)
'D'
>>> choice(L)
'F'
>>> choice(L)
'B'

explanation/notes

To use the random library, we first need to import it.

The randrange(start,stop) function picks a random number from start to stop - 1, so the above example is choosing a random number from 1-100.

The choice(sequence) function picks a random item from a sequence (here a python list).

Table 6. random Library Functions
function description example

randrange

pick random integer from range

randrange(1,11)

choice

pick random item from sequence

choice("ABCDEFG")

random

pick random number from [0-1)

random()

shuffle

shuffle list in place

shuffle(L)

another example:

>>> from random import *
>>> if random() >= 0.5:
...    print("Heads")
... else:
...    print("Tails")
...
Tails

See also:

while (indefinite) loops

example

Sometimes you want to repeat a code block, but you don’t know exactly how many times to repeat. For example, in a game like tic-tac-toe, you might want to get the player’s move, then update board, then get the next player’s move, and so on, until the game is over. The "get move, update board" code might be executed 5 or more times. This is called an indefinite loop.

Here’s an example using random numbers, so we don’t know how many times the code block will run, and each time we run this code the output will be different (because the random numbers chosen will be different):

from random import *

value = 0
limit = 10
while value < limit:
   print("value: %2d -- limit = %2d" % (value, limit))
   print("picking random number from 1-10...")
   randnumber = randrange(1,11)
   print(">>>>> picked %d" % randnumber)
   if randnumber > 6:
      value = value + 2
print("-"*30)
print("Done!")

Here’s one example of running the above code:

value:  0 -- limit = 10
picking random number from 1-10...
>>>>> picked 7
value:  3 -- limit = 10
picking random number from 1-10...
>>>>> picked 8
value:  6 -- limit = 10
picking random number from 1-10...
>>>>> picked 4
value:  6 -- limit = 10
picking random number from 1-10...
>>>>> picked 10
value:  9 -- limit = 10
picking random number from 1-10...
>>>>> picked 8
------------------------------
Done!

explanation/notes

The above while loop keeps picking random numbers as long as (while) value is less than limit. Note that in the code block (the indented code below the while value < limit line), it is possible that value will be changed (increased by 2), if the random number chosen is greater than 6.

The while loop syntax is:

while <boolean expression>:
   do this line
   and this indented line
   and this indented line

This looks just like the if statement, and it is, except that after the code block is run, we go back to the top and re-check the boolean expression. If the boolean expression is still True, we run the code block again. As long as (while) the boolean expression evaluates to True, we’ll keep running the code block. After each run of the code block, we re-check the boolean expression to see if it is True or False. Once it’s False, we skip the code block and continue on with the rest of the program.

Don’t confuse the if statement with the while loop!

if x < 20:                     while x < 20:
   do this line                   do this line

Both of those will execute the "do this line" if x is less than 20. The while loop will then go back and re-check if x is still less than 20, where the if code will continue on with the rest of the program.

What do you think this code will do?

x = 0
while x < 100:
   print(x)
   print("--------------------")

This is an infinite loop, that will just print 0 and a dashed line over and over and over. You can use Ctrl-c to kill a program stuck in an infinite loop.

another example

Have you ever used Duolingo, where the owl cheers you on, and notices when you give the correct answer three times in a row?

from random import *

factor = 9
inarow = 0
while inarow < 3:
   otherfactor = randrange(2,13)        # pick number 2-12
   answer = factor * otherfactor        # calculate the right answer
   question = "What is %d*%d? " % (factor,otherfactor)
   useranswer = int(input(question))    # get user's answer
   if useranswer == answer:
     inarow = inarow + 1
   else:
     print("Nope...answer = %d" % answer)
     inarow = 0
print("Three in a row...good work!")

And here’s one possible run of the above code:

What is 9*7? 63
What is 9*10? 90
What is 9*2? 19
Nope...answer = 18
What is 9*12? 108
What is 9*5? 45
What is 9*8? 72
Three in a row...good work!

See also:

Functions: advanced

Functions make your programs easier to read, write, design, debug, and test. Once you understand how functions work, they will make your programming life much much easier!

This section describes some important details about functions in python:

  • scope/variables

  • stack diagrams

  • mutable objects in functions

scope and variables

A variable’s scope refers to where a variable is valid. For example, in this program with two functions, main() and times2(), the variable factor is in scope (i.e., valid) only in times2():

def main():
   x = 5
   result = times2(x)
   print("result: ", result)
   print("     x: ", x)

def times2(factor):
   factor = factor*2
   return factor

main()

If we added a print(factor) statement in main(), the program would crash, since factor is not defined or valid in main():

  print(factor)
NameError: name 'factor' is not defined

Similarly, the variable x is only valid (in scope) in main(). Trying to use x in times2() would also cause a runtime error.

Furthermore, it does not matter what the factor variable in times2() is called! We could name it x, but that would be a different x than the one defined in main().

Here’s the code again, this time with the variable in times2() defined as x:

def main():
   x = 5
   result = times2(x)
   print("result: ", result)
   print("     x: ", x)

def times2(x):
   x = x*2
   return x

main()

And here’s the output from running that code:

result:  10
     x:  5

Notice that the x printed in main(), after the times2() function was called, still has the value 5 (even though the x in times2() was set to 10).

stack diagrams

You can easily see a variable’s scope if you view the program’s stack diagram using the python tutor. Here’s an image of the above code (with x in each function) running in the python tutor:

scope stack

On the right of that image are three stack frames: Global (which we are ignoring), main, and times2. We have used the python tutor "Forward>" button to step through the program, one statement at a time. First main() was called, then main() called times2() on line 3, and we are currently stopped at the end of times2() (line 9), about to return x.

For the stack, each time a function is called, a new stack frame is created and placed "on top" of the stack. All of that function’s variables are defined in that stack frame and have local scope. As you can see, there is an x in main() with value 5, and there is an x in times2() with value 10. Even though we used the same name, there are two different x variables. Each variable is local in scope to that function.

The stack is useful for seeing what variables are in scope, but also for keeping track of where we are in the execution of the program. If main() calls functionA() and functionA() calls functionB(), the stack would look like this:

stack

In the python tutor the stack is always drawn increasing toward the bottom of the window, but it is more useful to think of the stack as a stack of trays or dishes: the one on top is the current tray/dish (the one you would grab). The function on "top" of the stack is the one currently running. When that function finishes and returns, control drops back to the function one "below" on the stack.

This is how the "Back" button on your browser works, or the "Undo" option in an editor works. All visited web pages (or editor changes) are kept in a stack. The item on "top" of the stack is the current item. For the browser, if you hit the "Back" button, the page on top of the stack is removed and the browser goes back to the previous page (the next page down in the stack).

mutable objects

In python, mutable objects are not copied to local variables in functions. Instead, they are passed to functions by reference. This means, for example, if you send a python list to a function, and in that function you change an item in the list, that change will be visible back in main():

def main():
  L = list("ABCD")
  print(L)
  change2(L)
  print(L)

def change2(mylist):
  mylist[2] = "pony"
  # note...no return statement needed

main()

Here’s the result of running that program:

$ python3 function-list.py
['A', 'B', 'C', 'D']
['A', 'B', 'pony', 'D']

You can again easily see this in the python tutor:

mutable

See how both L in main() and mylist in change2() both point to the same list data! Both variables refer back to the same list stored in the memory of the computer. Assigning mylist[2] in change2() to some other value ("pony") has the side-effect of also changing L in main(), since they both refer back to the same list.

This feature is useful, but often confusing to first-time programmers.

It is useful, say, if you pass a 1-million-element list to a function. If python made a copy of that list, it would take time and be inefficient.

It is confusing because the function can change items in a list (or any items in a mutable python object), and those changes are seen outside of the function, and the function doesn’t have to return anything. This is exactly the opposite of what we talked about above, with variables in functions and local scope.

Here’s a simple example of using a function that modifies a list without returning anything from the function:

>>> from random import shuffle
>>> L = list("ABCDEFG")
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G']
>>> shuffle(L)
>>> print(L)
['E', 'B', 'F', 'C', 'A', 'G', 'D']
>>> shuffle(L)
>>> print(L)
['C', 'D', 'A', 'F', 'G', 'B', 'E']

See also:

Files

example

$ cat poem.txt
Roses are red.
Violets are blue.
Sugar is sweet.
And so are you!
$ python3
>>> infile = open("poem.txt", "r")
>>> lines = infile.readlines()
>>> print(lines)
['Roses are red.\n', 'Violets are blue.\n', 'Sugar is sweet.\n', 'And so are you!\n']

explanation/notes

The variable infile stores the file object returned by the open() function. We have opened the file named "poem.txt" for reading (using the "r" argument). The readlines() method reads in all lines from the file and returns them as a list of strings. We assign this list of strings to the lines variable. Note how each line from the file ends with a newline character (the '\n').

For file objects, here are the frequently-used methods:

Table 7. File Object Methods
method description example returns

readlines()

reads all lines from file

infile.readlines()

list of strings

readline()

reads one line from file

infile.readline()

string

close()

closes the file

infile.close()

nothing

write()

writes one line to file

outfile.write('hi!\n')

number of chars written

another example

Files are another example of a sequence in python (like strings and lists). We can use sequences in for loops, so we could also read in all lines from a file like this:

inf = open("poem.txt", "r")
lines = []
for line in inf:
   lines.append(line)
inf.close()

This does exactly the same thing as the previous example. It’s just shown here as an example of iterating over each line in the file. Sometimes you want to process the data line-by-line, as you read each line in (instead of reading them all in at once with readlines()).

writing to files

Similar to reading, but the file is opened with the "w" flag:

>>> outfile = open("newfile", "w")
>>> outfile.write("We love CompSci!!\n")
>>> outfile.write("Here's another line...\n")
>>> outfile.close()
>>>
$ cat newfile
We love CompSci!!
Here's another line...
$

Note how we have to include the newline character ('\n') at the end of each string we write. Otherwise the file would contain just one long line. Also, if the file already exists, and we open it for writing, any data already in the file is wiped out and replaced by whatever we write.


See also:

Classes

Classes allow us to design and create our own objects! Object-oriented programming (OOP) is crucial for large programs, often written by multiple people. It greatly simplifies writing, testing, and debugging if the objects/classes can be written and tested on their own, then used in larger programs. OOP also promotes code reuse if the objects can be used in other programs.

example

Imagine you are a teacher, and you want to keep track of your students and their grades for the semester! :)

Here is a Student object (in a file called student.py), where all students have a name, class year, username, quiz grades, and lab grades:

class Student(object):
  """student objects"""

  def __init__(self, name, year, username):
    """constructor, given student name, year, username"""

    self.name = name
    self.year = year
    self.username = username
    self.quizzes = []            # initially, no quiz
    self.labs = []               # or lab grades

  def __str__(self):
    """this one *must* return a string"""
    s = "%s (%s) -- %d" % (self.name, self.username, self.year)
    s += "\n%s\n%s" % (self.quizzes, self.labs)
    return s

  def addQuizGrade(self, qgrade):
    """add a given grade to the quizzes list"""
    self.quizzes.append(qgrade)

  def addLabGrade(self, lgrade):
    """add a given grade to the labs list"""
    self.labs.append(lgrade)

  def currentGrade(self):
    """
    calculate and return current grade.
    quizzes are 60%, labs 40% of total grade
    """
    quizavg = 0.0
    if len(self.quizzes) > 0:
      quizavg = sum(self.quizzes)/len(self.quizzes)
    labavg = 0.0
    if len(self.labs) > 0:
      labavg = sum(self.labs)/len(self.labs)
    grade = quizavg*.60 + labavg*.40
    return grade

#####################################################

def main():
  s1 = Student("Lee Majors", 2020, "lmajors1")
  print(s1)
  s2 = Student("Lindsay Wagner", 2022, "lwagner2")
  print(s2)
  s1.addQuizGrade(78)
  s1.addQuizGrade(75)
  s1.addQuizGrade(83)
  s1.addLabGrade(91)
  s1.addLabGrade(90)
  print(s1)
  print(s1.currentGrade())
  print(s2.currentGrade())

if __name__ == "__main__":
  main()

explanation/notes

The code in the def main() part at the bottom is solely for testing purposes (for the person writing the class), and it is not part of the class definition. It is run only if the file is run: python3 student.py, and not if the file is imported (from student import *). This test code is very simple, and just tests some of the methods. It also shows how to use the class.

the __init__ method

In the test code you can see two different student objects (s1 and s2) are created by calling the name of the class: Student(…​). Calling the name of the class invokes the constructor method: __init__(…​). This creates the object with the given data (name, year, username). In this particular case, it also creates the empty lists that will hold the student’s quiz and lab grades.

what is self???

All of the self.xxxxx variables in the constructor are data stored in each object. The self part refers back to which object (e.g., s1 or s2). So the class year 2020 is stored in the first object’s self.year variable, and the username "lwagner2" is stored in the second object’s self.username variable. If you want data stored in an object, create a self.xxxxx variable in the class constructor (the __init__() function). These self.xxxxx variables are sometimes called instance variables, because they exist for every instance of the class (i.e., all Student objects).

Note how the self.xxxxx variables are used in the other methods (like currentGrade()), and they don’t have to be passed around as arguments and parameters. Any self.xxxxx variable defined in the constructor (__init__()) can be used in any other method in the given class.

methods

After the objects are created, we can modify or access the data stored in the objects using the other methods in the class. For example, the teacher might want to add grades to each student object:

s1.addQuizGrade(83)
s1.addLabGrade(91)

These two methods add data to the self.quizzes and self.labs variables. Methods are just like functions, but they belong to a specific class, and are called using the object.method() syntax.

the __str__ method

The str method is automatically invoked anytime an object is printed (e.g., print(s1)). You can control how you want your object’s data to appear by changing the string that __str__ returns. This method is often very helpful when debugging a class, as you can print your objects and see what data they contain.

class Writer vs class User

In OOP, it is often useful to keep in mind these two roles: the person writing the class, and the person using the class. Sometimes you’re doing both, but other times you may just be using a class someone else wrote.

The methods in the class definition provide the class user with ways to interact with the object and it’s data. If you are writing a class, you need to think about how people will use the class, and provide methods for all that they might want to do. If you are using a class, you’ll need to study the methods for how best to use the objects, and what operations are possible.

another example

Suppose I want to write a stats program to keep track of data on all NBA players. I could make a python list for each player, where in the list I store the player’s name, team, total points scored, etc. But then my whole program would involve lists and indexing, and be much harder to read (e.g., player[0] would be the player’s name, player[1] would be the player’s team, etc).

Instead, let’s make an NBAPlayer object and store all data for a player in the object:

class NBAPlayer(object):
  """class for single NBA player object"""

  def __init__(self, name, number, team):
    """constructor for player object, given name, etc"""
    # any self.whatever variable is DATA for this object,
    # and can be used in any methods in this class
    self.name = name
    self.number = int(number)    # jersey number
    self.team = team
    self.gp  = 0                 # games played
    self.pts = 0                 # total points scored

  def __str__(self):
    """pretty-format info about this object, return a string"""
    s = "%s #%d -- Team: %s" % (self.name, self.number, self.team)
    s += "\nGP: %d,  PTS: %d" % (self.gp, self.pts)
    return s

  def playGame(self, points):
    """example of adding data to player object"""
    self.gp += 1           # one game played
    self.pts += points     # add to total points scored

  def ppg(self):
    """calculate average points per game"""
    if self.gp == 0:
      return 0
    else:
      ave = self.pts/float(self.gp)
      return ave

  def trade(self,newteam):
    """change team to newteam"""
    self.team = newteam

  def getName(self):
    """getter for player's name"""
    return self.name

  def getTeam(self):
    """getter for player's team"""
    return self.team

Now the user of this class could write code like this to keep data on a particular player:

from nbaplayer import *

p1 = NBAPlayer("Jeff Knerr", 11, "Philadelphia 76ers")
print(p1)
p1.playGame(20)    # a good game!
p1.playGame(10)    # so-so...
print(p1)

print("%s is averaging %5.1f points/game" % (p1.getName(), p1.ppg()))

p1.playGame(3)     # Jeff not playing well....let's trade him
p1.trade("New York Knicks")
print(p1)

Running the above code produces the following output:

Jeff Knerr #11 -- Team: Philadelphia 76ers
GP: 0,  PTS:  0
Jeff Knerr #11 -- Team: Philadelphia 76ers
GP: 2,  PTS: 30
Jeff Knerr is averaging  15.0 points/game
Jeff Knerr #11 -- Team: New York Knicks
GP: 3,  PTS: 33

See also:

Dictionaries

Dictionaries are great for storing key-value mappings or pairs.

example

Suppose you wanted to convert letters into their equivalent Morse Code dots and dashes:

>>> d = {}
>>> d['a'] = ".-"
>>> d['b'] = "-..."
>>> d['c'] = "-.-."
>>> print(d)
{'a': '.-', 'b': '-...', 'c': '-.-.'}
>>> print(d['a'])
.-

Here the letters ('a','b', and 'c') are the keys, and the dot/dash strings are the values. The first line creates an empty dictionary (using curly-braces). The next three lines add key-value pairs into the dictionary.

If we had the full Morse Code dictionary defined:

morse = {'a': '.-', 'b': '-...', 'c': '-.-.', 'd': '-..', 'e': '.',
         'f': '..-.', 'g': '--.', 'h': '....', 'i': '..', 'j': '.---',
         'k': '-.-', 'l': '.-..', 'm': '--', 'n': '-.', 'o': '---',
         'p': '.--.', 'q': '--.-', 'r': '.-.', 's': '...', 't': '-',
         'u': '..-', 'v': '...-', 'w': '.--', 'x': '-..-', 'y': '-.--',
         'z': '--..'}

then we could use it to lookup values, given letters. Here’s a quick example:

for ch in "sos":
   print(morse[ch])

This would print three dots for the 's', then three dashes for the 'o', and then another three dots for the last 's'.

explanation/notes

Dictionaries are useful for any kind of key-value lookup system. For example, usernames and passwords:

passwords = {'terry':'lovesYogurt', 'jake':'DieHard5',
             'amy':'MoreBinders', 'holt':'Cheddar!!'}

Given a username (e.g., 'jake'), we can lookup the password in the dictionary (passwords['jake']).

another example

There are a variety of useful methods for dictionary objects:

>>> passwords.keys()
dict_keys(['terry', 'jake', 'amy', 'holt'])
>>> passwords.values()
dict_values(['lovesYogurt', 'DieHard5', 'MoreBinders', 'Cheddar!!'])
>>> passwords.items()
dict_items([('terry', 'lovesYogurt'), ('jake', 'DieHard5'), ('amy', 'MoreBinders'), ('holt', 'Cheddar!!')])
>>> for k in passwords:
...    print(k, passwords[k])
...
terry lovesYogurt
jake DieHard5
amy MoreBinders
holt Cheddar!!

The last example shows dictionaries can be used in a for loop, with the loop variable becoming each key in the dictionary.


See also:

Tuples

Tuples are just like python lists, except they use parentheses, and are immutable.

example

>>> T = (1,2,3,4)
>>> print(T)
(1, 2, 3, 4)
>>> print(type(T))
<class 'tuple'>
>>> x = T[0] + T[2]
>>> print(x)
4
>>> T[0] = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>>

explanation/notes

Tuples are useful if you want a list-like data structure, but you want to make sure it can’t be changed later in the program. They can also be used as keys in Dictionaries.

We don’t use tuples much in CS21, but they are included here so you know what they are. Sometimes they are mentioned in error messages. Here’s the most-common error involving tuples in CS21 (forgot to use 'Point' when defining a graphics point object):

>>> from graphics import *
>>> p = (100,200)             # <-- should have been Point(100,200)
>>> c = Circle(p, 25)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
graphics.TypeError: When calling Circle constructor, argument should have
                    type Point but instead has type tuple.

another example

This is also not used much, and somewhat discouraged in CS21, but returning multiple items from a function will automatically put them in a tuple:

def minmax(L):
  """given a list, find and return min and max of list"""

  min = L[0]
  max = L[0]
  for item in L:
    if item < min:
      min = item
    if item > max:
      max = item
  return min,max

Here’s the output from running the above function:

>>> result = minmax([30,45,-4,200,5,6,37,99])
>>> print(result)
(-4, 200)
>>> print(type(result))
<class 'tuple'>

See also:

ord and chr

example

>>> ord('a')
97
>>> ord('b')
98
>>> ord('c')
99
>>> chr(97)
'a'
>>> chr(98)
'b'
>>> chr(65)
'A'

explanation/notes

Sometimes it is useful to compare letters and strings, or to do "math" on letters (e.g., if I have a letter, give me the 5th letter after the one I have).

The computer is able to compare letters and strings because, underneath, the computer just sees letters as numbers. This is called an encoding, and one of the most-famous encodings is the ASCII encoding:

Table 8. Decimal ASCII Chart by Joseph Steinhauser

000 nul

001 soh

002 stx

003 etx

004 eot

005 enq

006 ack

007 bel

008 bs

009 tab

010 nl

011 vt

012 np

013 cr

014 so

015 si

016 dle

017 dc1

018 dc2

019 dc3

020 dc4

021 nak

022 syn

023 etb

024 can

025 em

026 sub

027 esc

028 fs

029 gs

030 rs

031 us

032 sp

033 !

034 "

035 #

036 $

037 %

038 &

039 '

040 (

041 )

042 *

043

044 ,

045 -

046 .

047 /

048 0

049 1

050 2

051 3

052 4

053 5

054 6

055 7

056 8

057 9

058 :

059 ;

060 <

061 =

062 >

063 ?

064 @

065 A

066 B

067 C

068 D

069 E

070 F

071 G

072 H

073 I

074 J

075 K

076 L

077 M

078 N

079 O

080 P

081 Q

082 R

083 S

084 T

085 U

086 V

087 W

088 X

089 Y

090 Z

091 [

092 \

093 ]

094 ^

095 _

096 `

097 a

098 b

099 c

100 d

101 e

102 f

103 g

104 h

105 i

106 j

107 k

108 l

109 m

110 n

111 o

112 p

113 q

114 r

115 s

116 t

117 u

118 v

119 w

120 x

121 y

122 z

123 {

124 |

125 }

126 ~

127 del

So the computer sees the letter 'a' as 97, 'b' as 98, and 'c' as 99. That’s why these work:

>>> 'a' < 'b'
True
>>> 'b' < 'c'
True
>>> 'apple' < 'banana'
True
>>> 'apple' < 'azure'
True

ord() and chr() allow you to convert back and forth between letters (or punctuation and other characters) and their ASCII encoding numbers.

another example

 word = "dog"
 newword = ""
 for letter in word:
   newnumber = ord(letter) + 3
   newletter = chr(newnumber)
   newword = newword + newletter
 print(word, newword)

In the above example we use ord(letter) to turn the letter into a number, add 3 to it, then use chr(newnumber) to get the letter for that new number. If the letter were an 'a', the newletter would be a 'd'. This is often used in simple cipher programs to shift letters in a string by some amount. The above example would print "dog grj".

Note: if the word was "xyz", what would the new "word" be? To be a "rotation cipher" the above code would have to test if the new number was past the end of the alphabetic characters ('z' or 122), and if so, subtract 26 from the new number.

Also, note that all of the capital letters ('A' - 'Z') are before the lowercase letters, so when comparing strings, make sure the case is the same!

>>> "APPLE" < "apple"
True
>>> "banana" < "APPLE"
False
>>> "BANANA" < "apple"
True

See also:

try/except

In previous examples, when getting a number from the user, we just blindly converted it from a string to an int or a float:

age = float(input("How old are you? "))

This will of course crash the program if the user enters something that can’t be converted to a float:

How old are you? pony
Traceback (most recent call last):
  File "tryexcept.py", line 3, in <module>
    age = float(input("How old are you? "))
ValueError: could not convert string to float: 'pony'

The try/except statement gives us a way to catch the exception (the ValueError above) and do something about it (like give the user another try, and not just crash the program).

example

age = input("How old are you? ")
try:
   age = float(age)
except ValueError:
   print("Please enter a number...")

And here’s what happens when we run that:

How old are you? zebra
Please enter a number...

explanation/notes

In the above example we try to convert the input stored in age to a float. If that succeeds, great, and the program continues with any code below the try/except block. If that fails with the ValueError exception, the except block is executed (the print statement).

If we combine the above with a while loop, our input statment now repeats until we get valid input:

VALIDINPUT = False
while not VALIDINPUT:
   age = input("How old are you? ")
   try:
      age = float(age)
      VALIDINPUT = True
   except ValueError:
      print("Please enter a number...")
print("Thanks!")

Here we are using a boolean flag variable (VALIDINPUT) to control the while loop. The loop continues as long as VALIDINPUT is False. The only way VALIDINPUT changes to True is if the age=float(age) statement succeeds. Here’s how it looks to the user:

How old are you? pony
Please enter a number...
How old are you? zebra
Please enter a number...
How old are you? 98-5.2
Please enter a number...
How old are you? 54.2
Thanks!

another example

There are many different exceptions in python. You can use any of them in a try/except statement. Here’s another example, trying to open a file, using the FileNotFoundError exception:

VALIDFILE = False
while not VALIDFILE:
   filename = input("File to open: ")
   try:
      infile = open(filename, "r")
      VALIDFILE = True
   except FileNotFoundError:
      print("I couldn't find that file...")
print("Got it!")

See also:

assert()

The assert() function is a simple way to add semi-automatic testing to your programs and libraries. An assert() statement gives no output if the test succeeds, but gives an AssertionError if the test fails.

example

>>> assert(1==1)
>>> assert(1+1==2)
>>> assert(1+1!=3)
>>> assert(1+1==3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError
>>> assert("hello".upper()=="HELLO")
>>> from math import sqrt
>>> assert(sqrt(4)==2)

explanation/notes

In the above examples, only the fourth test fails (1+1==3) and crashes with an AssertionError. If tests that pass produce no output, you can add these statements to a test function in your code and periodically run the test function to make sure everything is still working (no news is good news).

another example

Suppose I wrote a binarySearch(x,L) function that looks for x in the list L, using a binary search. Here’s an example of a function I might add, to test all possible cases I can think of:

def testcode():
    """test the binarySearch function"""
    x = 5
    L = [1,2,3,4,5,6,7,8,9,10]
    letters = list("BCDEFGHIKLMNPQRSTWXY")
    assert(binarySearch(x,L)==True)
    assert(binarySearch(-20,L)==False)
    assert(binarySearch("F",letters)==True)
    assert(binarySearch("Z",letters)==False)
    assert(binarySearch("A",letters)==False)
    assert(binarySearch("A",["A"])==True)
    assert(binarySearch("A",[])==False)
    for item in L:
        assert(binarySearch(item,L)==True)
    for item in L:
        assert(binarySearch(item,letters)==False)
    print("All tests passed!")

If I run the testcode() function and see the "All tests passed!" message at the bottom, I feel confident that my search function is working. And if I ever make changes to my search function, I can just run the test code again to make sure I didn’t break anything.