0% found this document useful (0 votes)
18 views241 pages

Computer Major Sem 1

The document introduces programming in Python, emphasizing the importance of thinking like a computer scientist, which combines elements of mathematics, engineering, and natural science. It covers basic programming concepts such as arithmetic operators, expressions, types, and debugging, while also explaining how Python handles numbers and strings. Additionally, it encourages the use of virtual assistants for further learning and provides exercises to reinforce understanding.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views241 pages

Computer Major Sem 1

The document introduces programming in Python, emphasizing the importance of thinking like a computer scientist, which combines elements of mathematics, engineering, and natural science. It covers basic programming concepts such as arithmetic operators, expressions, types, and debugging, while also explaining how Python handles numbers and strings. Additionally, it encourages the use of virtual assistants for further learning and provides exercises to reinforce understanding.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 241

CHAPTER 1

Programming as a Way of Thinking

The first goal of this book is to teach you how to program in Python. But learning to
program means learning a new way to think, so the second goal of this book is to help
you think like a computer scientist. This way of thinking combines some of the best
features of mathematics, engineering, and natural science. Like mathematicians, com‐
puter scientists use formal languages to denote ideas—specifically computations. Like
engineers, they design things, assembling components into systems and evaluating
trade-offs among alternatives. Like scientists, they observe the behavior of complex
systems, form hypotheses, and test predictions.
We will start with the most basic elements of programming and work our way up. In
this chapter, we’ll see how Python represents numbers, letters, and words. And you’ll
learn to perform arithmetic operations.
You will also start to learn the vocabulary of programming, including terms like oper‐
ator, expression, value, and type. This vocabulary is important—you will need it to
understand the rest of the book, to communicate with other programmers, and to use
and understand virtual assistants.

Arithmetic Operators
An arithmetic operator is a symbol that represents an arithmetic computation. For
example, the plus sign, +, performs addition:

30 + 12

42

1
The minus sign, –, is the operator that performs subtraction:

43 - 1

42

The asterisk, *, performs multiplication:

6 * 7

42

And the forward slash, /, performs division:

84 / 2

42.0

Notice that the result of the division is 42.0 rather than 42. That’s because there are
two types of numbers in Python:

• integers, which represent whole numbers, and


• floating-point numbers, which represent numbers with a decimal point.

If you add, subtract, or multiply two integers, the result is an integer. But if you divide
two integers, the result is a floating-point number. Python provides another opera‐
tor, //, that performs integer division. The result of integer division is always an
integer:

84 // 2

42

Integer division is also called “floor division” because it always rounds down (toward
the “floor”):

85 // 2

42

2 | Chapter 1: Programming as a Way of Thinking


Finally, the operator ** performs exponentiation; that is, it raises a number to a
power:

7 ** 2

49

In some other languages, the caret, ^, is used for exponentiation, but in Python it is a
bitwise operator called XOR. If you are not familiar with bitwise operators, the result
might be unexpected:

7 ^ 2

I won’t cover bitwise operators in this book, but you can read about them at http://
wiki.python.org/moin/BitwiseOperators.

Expressions
A collection of operators and numbers is called an expression. An expression can
contain any number of operators and numbers. For example, here’s an expression that
contains two operators:

6 + 6 ** 2

42

Notice that exponentiation happens before addition. Python follows the order of
operations you might have learned in a math class: exponentiation happens before
multiplication and division, which happen before addition and subtraction.
In the following example, multiplication happens before addition:

12 + 5 * 6

42

If you want the addition to happen first, you can use parentheses:

(12 + 5) * 6

102

Every expression has a value. For example, the expression 6 * 7 has the value 42.

Expressions | 3
Arithmetic Functions
In addition to the arithmetic operators, Python provides a few functions that work
with numbers. For example, the round function takes a floating-point number and
rounds it off to the nearest whole number:

round(42.4)

42

round(42.6)

43

The abs function computes the absolute value of a number. For a positive number,
the absolute value is the number itself:

abs(42)

42

For a negative number, the absolute value is positive:

abs(-42)

42

When we use a function like this, we say we’re calling the function. An expression
that calls a function is a function call.
When you call a function, the parentheses are required. If you leave them out, you get
an error message:

abs 42

Cell In[18], line 1


abs 42
^
SyntaxError: invalid syntax

You can ignore the first line of this message; it doesn’t contain any information we
need to understand right now. The second line is the code that contains the error,
with a caret (^) beneath it to indicate where the error was discovered.

4 | Chapter 1: Programming as a Way of Thinking


The last line indicates that this is a syntax error, which means that there is something
wrong with the structure of the expression. In this example, the problem is that a
function call requires parentheses.
Let’s see what happens if you leave out the parentheses and the value:

abs

<function abs(x, /)>

A function name all by itself is a legal expression that has a value. When it’s displayed,
the value indicates that abs is a function, and it includes some additional information
I’ll explain later.

Strings
In addition to numbers, Python can also represent sequences of letters, which are
called strings because the letters are strung together like beads on a necklace. To write
a string, we can put a sequence of letters inside straight quotation marks:

'Hello'

'Hello'

It is also legal to use double quotation marks:

"world"

'world'

Double quotes make it easy to write a string that contains an apostrophe, which is the
same symbol as a straight quote:

"it's a small "

"it's a small "

Strings can also contain spaces, punctuation, and digits:

'Well, '

'Well, '

Strings | 5
The + operator works with strings; it joins two strings into a single string, which is
called concatenation:

'Well, ' + "it's a small " + 'world.'

"Well, it's a small world."

The * operator also works with strings; it makes multiple copies of a string and con‐
catenates them:

'Spam, ' * 4

'Spam, Spam, Spam, Spam, '

The other arithmetic operators don’t work with strings.


Python provides a function called len that computes the length of a string:

len('Spam')

Notice that len counts the letters between the quotes, but not the quotes.
When you create a string, be sure to use straight quotes. The backquote, also known
as a backtick, causes a syntax error:

`Hello`

Cell In[49], line 1


`Hello`
^
SyntaxError: invalid syntax

Smart quotes, also known as curly quotes, are also illegal.

Values and Types


So far we’ve seen three kinds of values:

• 2 is an integer,
• 42.0 is a floating-point number, and
• 'Hello' is a string.

6 | Chapter 1: Programming as a Way of Thinking


A kind of value is called a type. Every value has a type—or we sometimes say it
“belongs to” a type.
Python provides a function called type that tells you the type of any value. The type
of an integer is int:

type(2)

int

The type of a floating-point number is float:

type(42.0)

float

And the type of a string is str:

type('Hello, World!')

str

The types int, float, and str can be used as functions. For example, int can take a
floating-point number and convert it to an integer (always rounding down):

int(42.9)

42

And float can convert an integer to a floating-point value:

float(42)

42.0

Now, here’s something that can be confusing. What do you get if you put a sequence
of digits in quotes?

'126'

'126'

Values and Types | 7


It looks like a number, but it is actually a string:

type('126')

str

If you try to use it like a number, you might get an error:

'126' / 3

TypeError: unsupported operand type(s) for /: 'str' and 'int'

This example generates a TypeError, which means that the values in the expression,
which are called operands, have the wrong type. The error message indicates that
the / operator does not support the types of these values, which are str and int.
If you have a string that contains digits, you can use int to convert it to an integer:

int('126') / 3

42.0

If you have a string that contains digits and a decimal point, you can use float to
convert it to a floating-point number:

float('12.6')

12.6

When you write a large integer, you might be tempted to use commas between groups
of digits, as in 1,000,000. This is a legal expression in Python, but the result is not an
integer:

1,000,000

(1, 0, 0)

Python interprets 1,000,000 as a comma-separated sequence of integers. We’ll learn


more about this kind of sequence later.
You can use underscores to make large numbers easier to read:

1_000_000

1000000

8 | Chapter 1: Programming as a Way of Thinking


Formal and Natural Languages
Natural languages are the languages people speak, like English, Spanish, and French.
They were not designed by people; they evolved naturally.
Formal languages are languages that are designed by people for specific applications.
For example, the notation that mathematicians use is a formal language that is partic‐
ularly good at denoting relationships among numbers and symbols. Similarly, pro‐
gramming languages are formal languages that have been designed to express
computations.
Although formal and natural languages have some features in common there are
important differences:
Ambiguity
Natural languages are full of ambiguity, which people deal with by using contex‐
tual clues and other information. Formal languages are designed to be nearly or
completely unambiguous, which means that any program has exactly one mean‐
ing, regardless of context.
Redundancy
In order to make up for ambiguity and reduce misunderstandings, natural lan‐
guages use redundancy. As a result, they are often verbose. Formal languages are
less redundant and more concise.
Literalness
Natural languages are full of idiom and metaphor. Formal languages mean
exactly what they say.
Because we all grow up speaking natural languages, it is sometimes hard to adjust to
formal languages. Formal languages are more dense than natural languages, so it
takes longer to read them. Also, the structure is important, so it is not always best to
read from top to bottom, left to right. Finally, the details matter. Small errors in spell‐
ing and punctuation, which you can get away with in natural languages, can make a
big difference in a formal language.

Debugging
Programmers make mistakes. For whimsical reasons, programming errors are called
bugs and the process of tracking them down is called debugging.
Programming, and especially debugging, sometimes brings out strong emotions. If
you are struggling with a difficult bug, you might feel angry, sad, or embarrassed.
Preparing for these reactions might help you deal with them. One approach is to
think of the computer as an employee with certain strengths, like speed and

Debugging | 9
precision, and particular weaknesses, like lack of empathy and an inability to grasp
the big picture.
Your job is to be a good manager: find ways to take advantage of the strengths and
mitigate the weaknesses. And find ways to use your emotions to engage with the
problem, without letting your reactions interfere with your ability to work effectively.
Learning to debug can be frustrating, but it is a valuable skill that is useful for many
activities beyond programming. At the end of each chapter there is a section, like this
one, with my suggestions for debugging. I hope they help!

Glossary
arithmetic operator: A symbol, like + and *, that denotes an arithmetic operation
like addition or multiplication.
integer: A type that represents whole numbers.
floating-point: A type that represents numbers with fractional parts.
integer division: An operator, //, that divides two numbers and rounds down to an
integer.
expression: A combination of variables, values, and operators.
value: An integer, floating-point number, or string—or one of other kinds of values
we will see later.
function: A named sequence of statements that performs some useful operation.
Functions may or may not take arguments and may or may not produce a result.
function call: An expression—or part of an expression—that runs a function. It con‐
sists of the function name followed by an argument list in parentheses.
syntax error: An error in a program that makes it impossible to parse—and therefore
impossible to run.
string: A type that represents sequences of characters.
concatenation: Joining two strings end to end.
type: A category of values. The types we have seen so far are integers (type int),
floating-point numbers (type float), and strings (type str).
operand: One of the values on which an operator operates.
natural language: Any of the languages that people speak that evolved naturally.

10 | Chapter 1: Programming as a Way of Thinking


formal language: Any of the languages that people have designed for specific pur‐
poses, such as representing mathematical ideas or computer programs. All program‐
ming languages are formal languages.
bug: An error in a program.
debugging: The process of finding and correcting errors.

Exercises
Ask a Virtual Assistant
As you work through this book, there are several ways you can use a virtual assistant
or chatbot to help you learn:

• If you want to learn more about a topic in the chapter, or anything is unclear, you
can ask for an explanation.
• If you are having a hard time with any of the exercises, you can ask for help.

In each chapter, I’ll suggest exercises you can do with a virtual assistant, but I encour‐
age you to try things on your own and see what works for you.
Here are some topics you could ask a virtual assistant about:

• Earlier I mentioned bitwise operators but I didn’t explain why the value of 7 ^ 2
is 5. Try asking “What are the bitwise operators in Python?” or “What is the value
of 7 XOR 2?”
• I also mentioned the order of operations. For more details, ask “What is the order
of operations in Python?”
• The round function, which we used to round a floating-point number to the
nearest whole number, can take a second argument. Try asking “What are the
arguments of the round function?” or “How do I round pi off to three decimal
places?”
• There’s one more arithmetic operator I didn’t mention; try asking “What is the
modulus operator in Python?”

Most virtual assistants know about Python, so they answer questions like this pretty
reliably. But remember that these tools make mistakes. If you get code from a chatbot,
test it!

Exercises | 11
Exercise
You might wonder what round does if a number ends in 0.5. The answer is that it
sometimes rounds up and sometimes rounds down. Try these examples and see if you
can figure out what rule it follows:

round(42.5)

42

round(43.5)

44

If you are curious, ask a virtual assistant, “If a number ends in 0.5, does Python round
up or down?”

Exercise
When you learn about a new feature, you should try it out and make mistakes on
purpose. That way, you learn the error messages, and when you see them again, you
will know what they mean. It is better to make mistakes now and deliberately than
later and accidentally.

1. You can use a minus sign to make a negative number like -2. What happens if
you put a plus sign before a number? What about 2++2?
2. What happens if you have two values with no operator between them, like 4 2?
3. If you call a function like round(42.5), what happens if you leave out one or
both parentheses?

Exercise
Recall that every expression has a value, every value has a type, and we can use the
type function to find the type of any value.
What is the type of the value of the following expressions? Make your best guess for
each one, and then use type to find out.

• 765 • abs(-7.0)
• 2.718 • abs
• '2 pi' • int
• abs(-7) • type

12 | Chapter 1: Programming as a Way of Thinking


Exercise
The following questions give you a chance to practice writing arithmetic expressions:

1. How many seconds are there in 42 minutes 42 seconds?


2. How many miles are there in 10 kilometers? Hint: there are 1.61 kilometers in a
mile.
3. If you run a 10 kilometer race in 42 minutes 42 seconds, what is your average
pace in seconds per mile?
4. What is your average pace in minutes and seconds per mile?
5. What is your average speed in miles per hour?

If you already know about variables, you can use them for this exercise. If you don’t,
you can do the exercise without them—and then we’ll see them in the next chapter.

Exercises | 13
CHAPTER 2
Variables and Statements

In the previous chapter, we used operators to write expressions that perform arith‐
metic computations.
In this chapter, you’ll learn about variables and statements, the import statement, and
the print function. And I’ll introduce more of the vocabulary we use to talk about
programs, including “argument” and “module.”

Variables
A variable is a name that refers to a value. To create a variable, we can write an
assignment statement like this:

n = 17

An assignment statement has three parts: the name of the variable on the left, the
equals operator, =, and an expression on the right. In this example, the expression is
an integer. In the following example, the expression is a floating-point number:

pi = 3.141592653589793

And in the following example, the expression is a string:

message = 'And now for something completely different'

When you run an assignment statement, there is no output. Python creates the vari‐
able and gives it a value, but the assignment statement has no visible effect. However,
after creating a variable, you can use it as an expression. So we can display the value
of message like this:

15
message

'And now for something completely different'

You can also use a variable as part of an expression with arithmetic operators:

n + 25

42

2 * pi

6.283185307179586

And you can use a variable when you call a function:

round(pi)

len(message)

42

State Diagrams
A common way to represent variables on paper is to write the name with an arrow
pointing to its value:

This kind of figure is called a state diagram because it shows what state each of the
variables is in (think of it as the variable’s state of mind). We’ll use state diagrams
throughout the book to represent a model of how Python stores variables and their
values.

Variable Names
Variable names can be as long as you like. They can contain both letters and numbers,
but they can’t begin with a number. It is legal to use uppercase letters, but it is conven‐
tional to use only lowercase for variable names.

16 | Chapter 2: Variables and Statements


The only punctuation that can appear in a variable name is the underscore character,
_. It is often used in names with multiple words, such as your_name or air
speed_of_unladen_swallow.
If you give a variable an illegal name, you get a syntax error. The name million! is
illegal because it contains punctuation:

million! = 1000000

Cell In[15], line 1


million! = 1000000
^
SyntaxError: invalid syntax

76trombones is illegal because it starts with a number:

76trombones = 'big parade'

Cell In[16], line 1


76trombones = 'big parade'
^
SyntaxError: invalid decimal literal

class is also illegal, but it might not be obvious why:

class = 'Self-Defence Against Fresh Fruit'

Cell In[17], line 1


class = 'Self-Defence Against Fresh Fruit'
^
SyntaxError: invalid syntax

It turns out that class is a keyword, which is a special word used to specify the struc‐
ture of a program. Keywords can’t be used as variable names.
Here’s a complete list of Python’s keywords:
False await else import pass
None break except in raise
True class finally is return
and continue for lambda try
as def from nonlocal while
assert del global not with
async elif if or yield
You don’t have to memorize this list. In most development environments, keywords
are displayed in a different color; if you try to use one as a variable name, you’ll know.

Variable Names | 17
The import Statement
In order to use some Python features, you have to import them. For example, the fol‐
lowing statement imports the math module:

import math

A module is a collection of variables and functions. The math module provides a


variable called pi that contains the value of the mathematical constant denoted π. We
can display its value like this:

math.pi

3.141592653589793

To use a variable in a module, you have to use the dot operator (.) between the name
of the module and the name of the variable.
The math module also contains functions. For example, sqrt computes square roots:

math.sqrt(25)

5.0

And pow raises one number to the power of a second number:

math.pow(5, 2)

25.0

At this point we’ve seen two ways to raise a number to a power: we can use the
math.pow function or the exponentiation operator, **. Either one is fine, but the
operator is used more often than the function.

Expressions and Statements


So far, we’ve seen a few kinds of expressions. An expression can be a single value, like
an integer, floating-point number, or string. It can also be a collection of values and
operators. And it can include variable names and function calls. Here’s an expression
that includes several of these elements:

19 + n + round(math.pi) * 2

42

18 | Chapter 2: Variables and Statements


We have also seen a few kinds of statements. A statement is a unit of code that has an
effect, but no value. For example, an assignment statement creates a variable and
gives it a value, but the statement itself has no value:

n = 17

Similarly, an import statement has an effect—it imports a module so we can use the
values and functions it contains—but it has no visible effect:

import math

Computing the value of an expression is called evaluation. Running a statement is


called execution.

The print Function


When you evaluate an expression, the result is displayed:

n + 1

18

But if you evaluate more than one expression, only the value of the last one is dis‐
played:

n + 2
n + 3

20

To display more than one value, you can use the print function:

print(n+2)
print(n+3)

19
20

It also works with floating-point numbers and strings:

print('The value of pi is approximately')


print(math.pi)

The value of pi is approximately


3.141592653589793

The print Function | 19


You can also use a sequence of expressions separated by commas:

print('The value of pi is approximately', math.pi)

The value of pi is approximately 3.141592653589793

Notice that the print function puts a space between the values.

Arguments
When you call a function, the expression in parentheses is called an argument. Nor‐
mally I would explain why, but in this case the technical meaning of a term has
almost nothing to do with the common meaning of the word, so I won’t even try.
Some of the functions we’ve seen so far take only one argument, like int:

int('101')

101

Some take two, like math.pow:

math.pow(5, 2)

25.0

Some can take additional arguments that are optional. For example, int can take a
second argument that specifies the base of the number:

int('101', 2)

The sequence of digits 101 in base 2 represents the number 5 in base 10.
round also takes an optional second argument, which is the number of decimal places
to round off to:

round(math.pi, 3)

3.142

20 | Chapter 2: Variables and Statements


Some functions can take any number of arguments, like print:

print('Any', 'number', 'of', 'arguments')

Any number of arguments

If you call a function and provide too many arguments, that’s a TypeError:

float('123.0', 2)

TypeError: float expected at most 1 argument, got 2

If you provide too few arguments, that’s also a TypeError:

math.pow(2)

TypeError: pow expected 2 arguments, got 1

And if you provide an argument with a type the function can’t handle, that’s a Type
Error, too:

math.sqrt('123')

TypeError: must be real number, not str

This kind of checking can be annoying when you are getting started, but it helps you
detect and correct errors.

Comments
As programs get bigger and more complicated, they get more difficult to read. Formal
languages are dense, and it is often difficult to look at a piece of code and figure out
what it is doing and why.
For this reason, it is a good idea to add notes to your programs to explain in natural
language what the program is doing. These notes are called comments, and they start
with the # symbol:

# number of seconds in 42:42


seconds = 42 * 60 + 42

Comments | 21
In this case, the comment appears on a line by itself. You can also put comments at
the end of a line:

miles = 10 / 1.61 # 10 kilometers in miles

Everything from the # to the end of the line is ignored—it has no effect on the execu‐
tion of the program. Comments are most useful when they document non-obvious
features of the code. It is reasonable to assume that the reader can figure out what the
code does; it is more useful to explain why.
This comment is redundant with the code and useless:

v = 8 # assign 8 to v

This comment contains useful information that is not in the code:

v = 8 # velocity in miles per hour

Good variable names can reduce the need for comments, but long names can make
complex expressions hard to read, so there is a trade-off.

Debugging
Three kinds of errors can occur in a program: syntax errors, runtime errors, and
semantic errors. It is useful to distinguish among them in order to track them down
more quickly:
Syntax error
“Syntax” refers to the structure of a program and the rules about that structure. If
there is a syntax error anywhere in your program, Python does not run the pro‐
gram. It displays an error message immediately.
Runtime error
If there are no syntax errors in your program, it can start running. But if some‐
thing goes wrong, Python displays an error message and stops. This type of error
is called a runtime error. It is also called an exception because it indicates that
something exceptional has happened.
Semantic error
The third type of error is “semantic,” which means related to meaning. If there is
a semantic error in your program, it runs without generating error messages, but
it does not do what you intended. Identifying semantic errors can be tricky
because it requires you to work backward by looking at the output of the pro‐
gram and trying to figure out what it is doing.

22 | Chapter 2: Variables and Statements


As we’ve seen, an illegal variable name is a syntax error:

million! = 1000000

Cell In[43], line 1


million! = 1000000
^
SyntaxError: invalid syntax

If you use an operator with a type it doesn’t support, that’s a runtime error:

'126' / 3

TypeError: unsupported operand type(s) for /: 'str' and 'int'

Finally, here’s an example of a semantic error. Suppose we want to compute the aver‐
age of 1 and 3, but we forget about the order of operations and write this:

1 + 3 / 2

2.5

When this expression is evaluated, it does not produce an error message, so there is
no syntax error or runtime error. But the result is not the average of 1 and 3, so the
program is not correct. This is a semantic error because the program runs but it
doesn’t do what’s intended.

Glossary
variable: A name that refers to a value.
assignment statement: A statement that assigns a value to a variable.
state diagram: A graphical representation of a set of variables and the values they
refer to.
keyword: A special word used to specify the structure of a program.
import statement: A statement that reads a module file and creates a module object.
module: A file that contains Python code, including function definitions and some‐
times other statements.
dot operator: The operator, ., used to access a function in another module by speci‐
fying the module name followed by a dot and the function name.
statement: One or more lines of code that represent a command or action.
evaluate: Perform the operations in an expression in order to compute a value.

Glossary | 23
execute: Run a statement and do what it says.
argument: A value provided to a function when the function is called. Each argu‐
ment is assigned to the corresponding parameter in the function.
comment: Text included in a program that provides information about the program
but has no effect on its execution.
runtime error: An error that causes a program to display an error message and exit.
exception: An error that is detected while the program is running.
semantic error: An error that causes a program to do the wrong thing, but not to
display an error message.

Exercises
Ask a Virtual Assistant
Again, I encourage you to use a virtual assistant to learn more about any of the topics
in this chapter.
If you are curious about any of keywords I listed, you could ask “Why is class a key‐
word?” or “Why can’t variable names be keywords?”
You might have noticed that int, float, and str are not Python keywords. They are
variables that represent types, and they can be used as functions. So it is legal to have
a variable or function with one of those names, but it is strongly discouraged. Ask an
assistant “Why is it bad to use int, float, and string as variable names?”
Also ask, “What are the built-in functions in Python?” If you are curious about any of
them, ask for more information.
In this chapter we imported the math module and used some of the variables and
functions it provides. Ask an assistant, “What variables and functions are in the math
module?” and “Other than math, what modules are considered core Python?”

Exercise
Repeating my advice from the previous chapter, whenever you learn a new feature,
you should make errors on purpose to see what goes wrong.

1. We’ve seen that n = 17 is legal. What about 17 = n?


2. How about x = y = 1?
3. In some languages every statement ends with a semicolon (;). What happens if
you put a semicolon at the end of a Python statement?

24 | Chapter 2: Variables and Statements


4. What if you put a period at the end of a statement?
5. What happens if you spell the name of a module wrong and try to import maath?

Exercise
Practice using the Python interpreter as a calculator:
4
Part 1. The volume of a sphere with radius r is 3 πr3. What is the volume of a sphere
with radius 5? Start with a variable named radius and then assign the result to a vari‐
able named volume. Display the result. Add comments to indicate that radius is in
centimeters and volume is in cubic centimeters.
Part 2. A rule of trigonometry says that for any value of x, cos x 2 + sin x 2 = 1.
Let’s see if it’s true for a specific value of x like 42.
Create a variable named x with this value. Then use math.cos and math.sin to com‐
pute the sine and cosine of x, and the sum of their squares.
The result should be close to 1. It might not be exactly 1 because floating-point arith‐
metic is not exact—it is only approximately correct.
Part 3. In addition to pi, the other variable defined in the math module is e, which
represents the base of the natural logarithm, written in math notation as e. If you are
not familiar with this value, ask a virtual assistant “What is math.e?” Now let’s com‐
pute e2 three ways:

1. Use math.e and the exponentiation operator (**).


2. Use math.pow to raise math.e to the power 2.
3. Use math.exp, which takes as an argument a value, x, and computes ex.

You might notice that the last result is slightly different from the other two. See if you
can find out which is correct.

Exercises | 25
CHAPTER 3
Functions

In the previous chapter we used several functions provided by Python, like int and
float, and a few provided by the math module, like sqrt and pow. In this chapter, you
will learn how to create your own functions and run them. And we’ll see how one
function can call another. As examples, we’ll display lyrics from Monty Python songs.
These silly examples demonstrate an important feature—the ability to write your own
functions is the foundation of programming.
This chapter also introduces a new statement, the for loop, which is used to repeat a
computation.

Defining New Functions


A function definition specifies the name of a new function and the sequence of state‐
ments that run when the function is called. Here’s an example:

def print_lyrics():
print("I'm a lumberjack, and I'm okay.")
print("I sleep all night and I work all day.")

def is a keyword that indicates that this is a function definition. The name of the
function is print_lyrics. Anything that’s a legal variable name is also a legal func‐
tion name.
The empty parentheses after the name indicate that this function doesn’t take any
arguments.
The first line of the function definition is called the header—the rest is called the
body. The header has to end with a colon and the body has to be indented. By con‐
vention, indentation is always four spaces. The body of this function is two print

27
statements; in general, the body of a function can contain any number of statements
of any kind.
Defining a function creates a function object, which we can display like this:

print_lyrics

<function __main__.print_lyrics()>

The output indicates that print_lyrics is a function that takes no arguments.


__main__ is the name of the module that contains print_lyrics.
Now that we’ve defined a function, we can call it the same way we call built-in
functions:

print_lyrics()

I'm a lumberjack, and I'm okay.


I sleep all night and I work all day.

When the function runs, it executes the statements in the body, which display the first
two lines of “The Lumberjack Song.”

Parameters
Some of the functions we have seen require arguments; for example, when you call
abs you pass a number as an argument. Some functions take more than one argu‐
ment; for example, math.pow takes two, the base and the exponent.
Here is a definition for a function that takes an argument:

def print_twice(string):
print(string)
print(string)

The variable name in parentheses is a parameter. When the function is called, the
value of the argument is assigned to the parameter. For example, we can call
print_twice like this:

print_twice('Dennis Moore, ')

Dennis Moore,
Dennis Moore,

28 | Chapter 3: Functions
Running this function has the same effect as assigning the argument to the parameter
and then executing the body of the function, like this:

string = 'Dennis Moore, '


print(string)
print(string)

Dennis Moore,
Dennis Moore,

You can also use a variable as an argument:

line = 'Dennis Moore, '


print_twice(line)

Dennis Moore,
Dennis Moore,

In this example, the value of line gets assigned to the parameter string.

Calling Functions
Once you have defined a function, you can use it inside another function. To demon‐
strate, we’ll write functions that print the lyrics of “The Spam Song”:
Spam, Spam, Spam, Spam,
Spam, Spam, Spam, Spam,
Spam, Spam,
(Lovely Spam, Wonderful Spam!)
Spam, Spam,

We’ll start with the following function, which takes two parameters:

def repeat(word, n):


print(word * n)

We can use this function to print the first line of the song, like this:

spam = 'Spam, '


repeat(spam, 4)

Spam, Spam, Spam, Spam,

Calling Functions | 29
To display the first two lines, we can define a new function that uses repeat:

def first_two_lines():
repeat(spam, 4)
repeat(spam, 4)

And then call it like this:

first_two_lines()

Spam, Spam, Spam, Spam,


Spam, Spam, Spam, Spam,

To display the last three lines, we can define another function, which also uses
repeat:

def last_three_lines():
repeat(spam, 2)
print('(Lovely Spam, Wonderful Spam!)')
repeat(spam, 2)

last_three_lines()

Spam, Spam,
(Lovely Spam, Wonderful Spam!)
Spam, Spam,

Finally, we can bring it all together with one function that prints the whole verse:

def print_verse():
first_two_lines()
last_three_lines()

print_verse()

Spam, Spam, Spam, Spam,


Spam, Spam, Spam, Spam,
Spam, Spam,
(Lovely Spam, Wonderful Spam!)
Spam, Spam,

When we run print_verse, it calls first_two_lines, which calls repeat, which calls
print. That’s a lot of functions.
Of course, we could have done the same thing with fewer functions, but the point of
this example is to show how functions can work together.

30 | Chapter 3: Functions
Repetition
If we want to display more than one verse, we can use a for statement. Here’s a simple
example:

for i in range(2):
print(i)

0
1

The first line is a header that ends with a colon. The second line is the body, which
has to be indented.
The first line starts with the keyword for, a new variable named i, and another key‐
word, in. It uses the range function to create a sequence of two values, which are 0
and 1. In Python, when we start counting, we usually start from 0.
When the for statement runs, it assigns the first value from range to i and then runs
the print function in the body, which displays 0.
When it gets to the end of the body, it loops back around to the header, which is why
this statement is called a loop. The second time through the loop, it assigns the next
value from range to i, and displays it. Then, because that’s the last value from range,
the loop ends.
Here’s how we can use a for loop to print two verses of the song:

for i in range(2):
print("Verse", i)
print_verse()
print()

Verse 0
Spam, Spam, Spam, Spam,
Spam, Spam, Spam, Spam,
Spam, Spam,
(Lovely Spam, Wonderful Spam!)
Spam, Spam,

Verse 1
Spam, Spam, Spam, Spam,
Spam, Spam, Spam, Spam,
Spam, Spam,
(Lovely Spam, Wonderful Spam!)
Spam, Spam,

Repetition | 31
You can put a for loop inside a function. For example, print_n_verses takes a
parameter named n, which has to be an integer, and displays the given number of
verses:

def print_n_verses(n):
for i in range(n):
print_verse()
print()

In this example, we don’t use i in the body of the loop, but there has to be a variable
name in the header anyway.

Variables and Parameters Are Local


When you create a variable inside a function, it is local, which means that it only
exists inside the function. For example, the following function takes two arguments,
concatenates them, and prints the result twice:

def cat_twice(part1, part2):


cat = part1 + part2
print_twice(cat)

Here’s an example that uses it:

line1 = 'Always look on the '


line2 = 'bright side of life.'
cat_twice(line1, line2)

Always look on the bright side of life.


Always look on the bright side of life.

When cat_twice runs, it creates a local variable named cat, which is destroyed when
the function ends. If we try to display it, we get a NameError:

print(cat)

NameError: name 'cat' is not defined

Outside of the function, cat is not defined.


Parameters are also local. For example, outside cat_twice, there is no such thing as
part1 or part2.

32 | Chapter 3: Functions
CHAPTER 5
Conditionals and Recursion

The main topic of this chapter is the if statement, which executes different code
depending on the state of the program. With the if statement we’ll be able to explore
one of the most powerful ideas in computing, recursion.
But we’ll start with three new features: the modulus operator, boolean expressions,
and logical operators.

Integer Division and Modulus


Recall that the integer division operator, //, divides two numbers and rounds down
to an integer. For example, suppose the runtime of a movie is 105 minutes. You might
want to know how long that is in hours. Conventional division returns a floating-
point number:

minutes = 105
minutes / 60

1.75

But we don’t normally write hours with decimal points. Floor division returns the
integer number of hours, rounding down:

minutes = 105
hours = minutes // 60
hours

55
To get the remainder, you could subtract off one hour, in minutes:

remainder = minutes - hours * 60


remainder

45

Or you could use the modulus operator, %, which divides two numbers and returns
the remainder:

remainder = minutes % 60
remainder

45

The modulus operator is more useful than it might seem. For example, it can check
whether one number is divisible by another: if x % y is zero, then x is divisible by y.
Also, it can extract the rightmost digit or digits from a number. For example, x % 10
yields the rightmost digit of x (in base 10). Similarly, x % 100 yields the last two
digits.
Finally, the modulus operator can do “clock arithmetic.” For example, if an event
starts at 11 A.M. and lasts three hours, we can use the modulus operator to figure out
what time it ends:

start = 11
duration = 3
end = (start + duration) % 12
end

The event would end at 2 P.M.:

a = 25 // 10
b = 25 % 10
a, b

(2, 5)

Boolean Expressions
A boolean expression is an expression that is either true or false. For example, the
following expressions use the equals operator, ==, which compares two values and
produces True if they are equal and False otherwise:

56 | Chapter 5: Conditionals and Recursion


5 == 5

True

5 == 7

False

A common error is to use a single equals sign (=) instead of a double equals sign (==).
Remember that = assigns a value to a variable and == compares two values:

x = 5
y = 7

x == y

False

True and False are special values that belong to the type bool; they are not strings:

type(True)

bool

type(False)

bool

The == operator is one of the relational operators; the others are:

x != y # x is not equal to y

True

x > y # x is greater than y

False

x < y # x is less than to y

True

Boolean Expressions | 57
x >= y # x is greater than or equal to y

False

x <= y # x is less than or equal to y

True

Logical Operators
To combine boolean values into expressions, we can use logical operators. The most
common are and, or, and not. The meaning of these operators is similar to their
meaning in English. For example, the value of the following expression is True only if
x is greater than 0 and less than 10:

x > 0 and x < 10

True

The following expression is True if either or both of the conditions is true, that is, if
the number is divisible by 2 or 3:

x % 2 == 0 or x % 3 == 0

False

Finally, the not operator negates a boolean expression, so the following expression is
True if x > y is False:

not x > y

True

Strictly speaking, the operands of a logical operator should be boolean expressions,


but Python is not very strict. Any nonzero number is interpreted as True:

42 and True

True

This flexibility can be useful, but there are some subtleties to it that can be confusing.
You might want to avoid it.

58 | Chapter 5: Conditionals and Recursion


if Statements
In order to write useful programs, we almost always need the ability to check condi‐
tions and change the behavior of the program accordingly. Conditional statements
give us this ability. The simplest form is the if statement:

if x > 0:
print('x is positive')

x is positive

if is a Python keyword. if statements have the same structure as function defini‐


tions: a header followed by an indented statement or sequence of statements called a
block.
The boolean expression after if is called the condition. If it is true, the statements in
the indented block run. If not, they don’t.
There is no limit to the number of statements that can appear in the block, but there
has to be at least one. Occasionally, it is useful to have a block that does nothing—
usually as a place keeper for code you haven’t written yet. In that case, you can use the
pass statement, which does nothing:

if x < 0:
pass # TODO: need to handle negative values!

The word TODO in a comment is a conventional reminder that there’s something you
need to do later.

The else Clause


An if statement can have a second part, called an else clause. The syntax looks like
this:

if x % 2 == 0:
print('x is even')
else:
print('x is odd')

x is odd

If the condition is true, the first indented statement runs; otherwise, the second
indented statement runs.

The else Clause | 59


In this example, if x is even, the remainder when x is divided by 2 is 0, so the condi‐
tion is true and the program displays x is even. If x is odd, the remainder is 1, so the
condition is false, and the program displays x is odd.
Since the condition must be true or false, exactly one of the alternatives will run. The
alternatives are called branches.

Chained Conditionals
Sometimes there are more than two possibilities and we need more than two
branches. One way to express a computation like that is a chained conditional, which
includes an elif clause:

if x < y:
print('x is less than y')
elif x > y:
print('x is greater than y')
else:
print('x and y are equal')

x is less than y

elif is an abbreviation of “else if.” There is no limit on the number of elif clauses. If
there is an else clause, it has to be at the end, but there doesn’t have to be one.
Each condition is checked in order. If the first is false, the next is checked, and so on.
If one of them is true, the corresponding branch runs and the if statement ends.
Even if more than one condition is true, only the first true branch runs.

Nested Conditionals
One conditional can also be nested within another. We could have written the exam‐
ple in the previous section like this:

if x == y:
print('x and y are equal')
else:
if x < y:
print('x is less than y')
else:
print('x is greater than y')

x is less than y

60 | Chapter 5: Conditionals and Recursion


The outer if statement contains two branches. The first branch contains a simple
statement. The second branch contains another if statement, which has two
branches of its own. Those two branches are both simple statements, although they
could have been conditional statements as well.
Although the indentation of the statements makes the structure apparent, nested
conditionals can be difficult to read. I suggest you avoid them when you can.
Logical operators often provide a way to simplify nested conditional statements.
Here’s an example with a nested conditional:

if 0 < x:
if x < 10:
print('x is a positive single-digit number.')

x is a positive single-digit number.

The print statement runs only if we make it past both conditionals, so we get the
same effect with the and operator:

if 0 < x and x < 10:


print('x is a positive single-digit number.')

x is a positive single-digit number.

For this kind of condition, Python provides a more concise option:

if 0 < x < 10:


print('x is a positive single-digit number.')

x is a positive single-digit number.

Recursion
It is legal for a function to call itself. It may not be obvious why that is a good thing,
but it turns out to be one of the most magical things a program can do. Here’s an
example:

def countdown(n):
if n <= 0:
print('Blastoff!')
else:
print(n)
countdown(n-1)

If n is 0 or negative, countdown outputs the word, “Blastoff!”. Otherwise, it outputs n


and then calls itself, passing n-1 as an argument.

Recursion | 61
If a program exceeds the limit, it causes a runtime error:

recurse()

---------------------------------------------------------------------------
RecursionError Traceback (most recent call last)
Cell In[41], line 1
----> 1 recurse()

Cell In[39], line 2, in recurse()


1 def recurse():
----> 2 recurse()

Cell In[39], line 2, in recurse()


1 def recurse():
----> 2 recurse()

[... skipping similar frames: recurse at line 2 (2958 times)]

Cell In[39], line 2, in recurse()


1 def recurse():
----> 2 recurse()
RecursionError: maximum recursion depth exceeded

The traceback indicates that there were almost three thousand frames on the stack
when the error occurred.
If you encounter an infinite recursion by accident, review your function to confirm
that there is a base case that does not make a recursive call. And if there is a base case,
check whether you are guaranteed to reach it.

Keyboard Input
The programs we have written so far accept no input from the user. They just do the
same thing every time.
Python provides a built-in function called input that stops the program and waits for
the user to type something. When the user presses Return or Enter the program
resumes, and input returns what the user typed as a string:

text = input()

64 | Chapter 5: Conditionals and Recursion


Before getting input from the user, you might want to display a prompt telling the
user what to type. input can take a prompt as an argument:

name = input('What...is your name?\n')


name

What...is your name?


It is Arthur, King of the Britons

'It is Arthur, King of the Britons'

The sequence \n at the end of the prompt represents a newline, which is a special
character that causes a line break—that way the user’s input appears below the
prompt.
If you expect the user to type an integer, you can use the int function to convert the
return value to int:

prompt = 'What...is the airspeed velocity of an unladen swallow?\n'


speed = input(prompt)
speed

What...is the airspeed velocity of an unladen swallow?


What do you mean: an African or European swallow?

'What do you mean: an African or European swallow?'

But if they type something that’s not an integer, you’ll get a runtime error.

int(speed)

ValueError: invalid literal for int() with base 10: 'What do you mean:
an African or European swallow?'

We will see how to handle this kind of error later.

Debugging
When a syntax or runtime error occurs, the error message contains a lot of informa‐
tion, but it can be overwhelming. The most useful parts are usually:

• What kind of error it was, and


• Where it occurred.

Debugging | 65
Syntax errors are usually easy to find, but there are a few gotchas. Errors related to
spaces and tabs can be tricky because they are invisible and we are used to ignoring
them:

x = 5
y = 6

Cell In[50], line 2


y = 6
^
IndentationError: unexpected indent

In this example, the problem is that the second line is indented by one space. But the
error message points to y, which is misleading. Error messages indicate where the
problem was discovered, but the actual error might be earlier in the code.
The same is true of runtime errors. For example, suppose you are trying to convert a
ratio to decibels, like this:

import math
numerator = 9
denominator = 10
ratio = numerator // denominator
decibels = 10 * math.log10(ratio)

---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[52], line 5
3 denominator = 10
4 ratio = numerator // denominator
----> 5 decibels = 10 * math.log10(ratio)

ValueError: math domain error

The error message indicates line 5, but there is nothing wrong with that line. The
problem is in line 4, which uses floor division instead of floating-point division—as a
result, the value of ratio is 0. When we call math.log10, we get a ValueError with
the message math domain error, because 0 is not in the “domain” of valid arguments
for math.log10, because the logarithm of 0 is undefined.
In general, you should take the time to read error messages carefully, but don’t
assume that everything they say is correct.

Glossary
recursion: The process of calling the function that is currently executing.
modulus operator: An operator, %, that works on integers and returns the remainder
when one number is divided by another.

66 | Chapter 5: Conditionals and Recursion


distance(1, 2, 4, 6)

5.0

And if we assign the result to a variable, nothing is displayed:

d = distance(1, 2, 4, 6)

The print statements we wrote are useful for debugging, but once the function is
working, we can remove them. Code like that is called scaffolding because it is help‐
ful for building the program but is not part of the final product. This example dem‐
onstrates incremental development. The key aspects of this process are:

1. Start with a working program, make small changes, and test after every change.
2. Use variables to hold intermediate values so you can display and check them.
3. Once the program is working, remove the scaffolding.

At any point, if there is an error, you should have a good idea where it is. Incremental
development can save you a lot of debugging time.

Boolean Functions
Functions can return the boolean values True and False, which is often convenient
for encapsulating a complex test in a function. For example, is_divisible checks
whether x is divisible by y with no remainder:

def is_divisible(x, y):


if x % y == 0:
return True
else:
return False

Here’s how we use it:

is_divisible(6, 4)

False

is_divisible(6, 3)

True

80 | Chapter 6: Return Values


Inside the function, the result of the == operator is a boolean, so we can write the
function more concisely by returning it directly:

def is_divisible(x, y):


return x % y == 0

Boolean functions are often used in conditional statements:

if is_divisible(6, 2):
print('divisible')

divisible

It might be tempting to write something like this:

if is_divisible(6, 2) == True:
print('divisible')

divisible

But the comparison is unnecessary.

Recursion with Return Values


Now that we can write functions with return values, we can write recursive functions
with return values, and with that capability, we have passed an important threshold—
the subset of Python we have is now Turing complete, which means that we can per‐
form any computation that can be described by an algorithm.
To demonstrate recursion with return values, we’ll evaluate a few recursively defined
mathematical functions. A recursive definition is similar to a circular definition, in
the sense that the definition refers to the thing being defined. A truly circular defini‐
tion is not very useful:
vorpal: An adjective used to describe something that is vorpal.

If you saw that definition in the dictionary, you might be annoyed. On the other
hand, if you looked up the definition of the factorial function, denoted with the sym‐
bol !, you might get something like this:

0! = 1
n! = n n − 1 !

This definition says that the factorial of 0 is 1, and the factorial of any other value, n,
is n multiplied by the factorial of n − 1.

Recursion with Return Values | 81


CHAPTER 7
Iteration and Search

In 1939, Ernest Vincent Wright published a 50,000-word novel called Gadsby that
does not contain the letter “e.” Since “e” is the most common letter in English, writing
even a few words without using it is difficult. To get a sense of how difficult, in this
chapter we’ll compute the fraction of English words have at least one “e.”
For that, we’ll use for statements to loop through the letters in a string and the words
in a file, and we’ll update variables in a loop to count the number of words that con‐
tain an “e.” We’ll use the in operator to check whether a letter appears in a word, and
you’ll learn a programming pattern called a “linear search.”
As an exercise, you’ll use these tools to solve a word puzzle called “Spelling Bee.”

Loops and Strings


In Chapter 3 we saw a for loop that uses the range function to display a sequence of
numbers:

for i in range(3):
print(i, end=' ')

0 1 2

This version uses the keyword argument end, so the print function puts a space after
each number rather than a newline.

91
We can also use a for loop to display the letters in a string:

for letter in 'Gadsby':


print(letter, end=' ')

G a d s b y

Notice that I changed the name of the variable from i to letter, which provides
more information about the value it refers to. The variable defined in a for loop is
called the loop variable.
Now that we can loop through the letters in a word, we can check whether it contains
the letter “e”:

for letter in "Gadsby":


if letter == 'E' or letter == 'e':
print('This word has an "e"')

Before we go on, let’s encapsulate that loop in a function:

def has_e():
for letter in "Gadsby":
if letter == 'E' or letter == 'e':
print('This word has an "e"')

And let’s make it a pure function that returns True if the word contains an “e” and
False otherwise:

def has_e():
for letter in "Gadsby":
if letter == 'E' or letter == 'e':
return True
return False

We can generalize it to take the word as a parameter:

def has_e(word):
for letter in word:
if letter == 'E' or letter == 'e':
return True
return False

92 | Chapter 7: Iteration and Search


Now we can test it like this:

has_e('Gadsby')

False

has_e('Emma')

True

Reading the Word List


To see how many words contain an “e,” we’ll need a word list. The one we’ll use is a
list of about 114,000 official crosswords; that is, words that are considered valid in
crossword puzzles and other word games.
The word list is in a file called words.txt, which is downloaded in the notebook for
this chapter. To read it, we’ll use the built-in function open, which takes the name of
the file as a parameter and returns a file object we can use to read the file:

file_object = open('words.txt')

The file object provides a function called readline, which reads characters from the
file until it gets to a newline and returns the result as a string:

file_object.readline()

'aa\n'

Notice that the syntax for calling readline is different from functions we’ve seen so
far. That’s because it is a method, which is a function associated with an object. In this
case readline is associated with the file object, so we call it using the name of the
object, the dot operator, and the name of the method.
The first word in the list is “aa,” which is a type of lava. The sequence \n represents
the newline character that separates this word from the next.
The file object keeps track of where it is in the file, so if you call readline again, you
get the next word:

line = file_object.readline()
line

'aah\n'

Reading the Word List | 93


To remove the newline from the end of the word, we can use strip, which is a
method associated with strings, so we can call it like this:

word = line.strip()
word

'aah'

strip removes whitespace characters—including spaces, tabs, and newlines—from


the beginning and end of the string.
You can also use a file object as part of a for loop. This program reads words.txt and
prints each word, one per line:

for line in open('words.txt'):


word = line.strip()
print(word)

Now that we can read the word list, the next step is to count the words. For that, we
will need the ability to update variables.

Updating Variables
As you may have discovered, it is legal to make more than one assignment to the
same variable. A new assignment makes an existing variable refer to a new value (and
stop referring to the old value).
For example, here is an initial assignment that creates a variable:

x = 5
x

And here is an assignment that changes the value of a variable:

x = 7
x

The following figure shows what these assignments look like in a state diagram:

94 | Chapter 7: Iteration and Search


The dotted arrow indicates that x no longer refers to 5. The solid arrow indicates that
it now refers to 7.
A common kind of assignment is an update, where the new value of the variable
depends on the old:

x = x + 1
x

This statement means “get the current value of x, add one, and assign the result back
to x.”
If you try to update a variable that doesn’t exist, you get an error, because Python
evaluates the expression on the right before it assigns a value to the variable on the
left:

y = y + 1

Before you can update a variable, you have to initialize it, usually with a simple
assignment:

y = 0
y = y + 1
y

Increasing the value of a variable is called an increment; decreasing the value is called
a decrement.

Looping and Counting


The following program counts the number of words in the word list:

total = 0

for line in open('words.txt'):


word = line.strip()
total = total + 1

Looping and Counting | 95


It starts by initializing total to 0. Each time through the loop, it increments total by
1. So when the loop exits, total refers to the total number of words:

total

113783

A variable like this, used to count the number of times something happens, is called a
counter.
We can add a second counter to the program to keep track of the number of words
that contain an “e”:

total = 0
count = 0

for line in open('words.txt'):


word = line.strip()
total = total + 1
if has_e(word):
count = count + 1

Let’s see how many words contain an “e”:

count

76162

As a percentage of total, about two-thirds of the words use the letter “e”:

count / total * 100

66.93618554617122

So you can understand why it’s difficult to craft a book without using any such words.

The in Operator
The version of has_e we wrote in this chapter is more complicated than it needs to
be. Python provides an operator, in, that checks whether a character appears in a
string:

word = 'Gadsby'
'e' in word

False

96 | Chapter 7: Iteration and Search


So we can rewrite has_e like this:

def has_e(word):
if 'E' in word or 'e' in word:
return True
else:
return False

And because the conditional of the if statement has a boolean value, we can elimi‐
nate the if statement and return the boolean directly:

def has_e(word):
return 'E' in word or 'e' in word

We can simplify this function even more using the method lower, which converts the
letters in a string to lowercase. Here’s an example:

word.lower()

'gadsby'

lower makes a new string—it does not modify the existing string—so the value of
word is unchanged:

word

'Gadsby'

Here’s how we can use lower in has_e:

def has_e(word):
return 'e' in word.lower()

has_e('Gadsby')

False

has_e('Emma')

True

The in Operator | 97
Search
Based on this simpler version of has_e, let’s write a more general function called
uses_any that takes a second parameter that is a string of letters. It returns True if the
word uses any of the letters, and False otherwise:

def uses_any(word, letters):


for letter in word.lower():
if letter in letters.lower():
return True
return False

Here’s an example where the result is True:

uses_any('banana', 'aeiou')

True

And another where it is False:

uses_any('apple', 'xyz')

False

uses_only converts word and letters to lowercase, so it works with any combination
of cases:

uses_any('Banana', 'AEIOU')

True

The structure of uses_any is similar to has_e. It loops through the letters in word and
checks them one at a time. If it finds one that appears in letters, it returns True
immediately. If it gets all the way through the loop without finding any, it returns
False.
This pattern is called a linear search. In the exercises at the end of this chapter, you’ll
write more functions that use this pattern.

98 | Chapter 7: Iteration and Search


108 INTRODUCTION TO COMPUTATION AND PROGRAMMING USING PYTHON

def getGrades(fname):
try:
gradesFile = open(fname, 'r') #open file for reading
except IOError:
raise ValueError('getGrades could not open ' + fname)
grades = []
for line in gradesFile:
try:
grades.append(float(line))
except:
raise ValueError('Unable to convert line to float')
return grades

try:
grades = getGrades('quiz1grades.txt')
grades.sort()
median = grades[len(grades)//2]
print('Median grade is', median)
except ValueError as errorMsg:
print('Whoops.', errorMsg)

Figure 7.3 Get grades

7.3 Assertions
The Python assert statement provides programmers with a simple way to con-
firm that the state of a computation is as expected. An assert statement can take
one of two forms:
assert Boolean expression
or
assert Boolean expression, argument
When an assert statement is encountered, the Boolean expression is evaluat-
ed. If it evaluates to True, execution proceeds on its merry way. If it evaluates to
False, an AssertionError exception is raised.
Assertions are a useful defensive programming tool. They can be used to
confirm that the arguments to a function are of appropriate types. They are also a
useful debugging tool. The can be used, for example, to confirm that intermedi-
ate values have the expected values or that a function returns an acceptable value.
9 A SIMPLISTIC INTRODUCTION TO ALGORITHMIC
COMPLEXITY

The most important thing to think about when designing and implementing a
program is that it should produce results that can be relied upon. We want our
bank balances to be calculated correctly. We want the fuel injectors in our auto-
mobiles to inject appropriate amounts of fuel. We would prefer that neither air-
planes nor operating systems crash.
Sometimes performance is an important aspect of correctness. This is most
obvious for programs that need to run in real time. A program that warns air-
planes of potential obstructions needs to issue the warning before the obstruc-
tions are encountered. Performance can also affect the utility of many non-real-
time programs. The number of transactions completed per minute is an im-
portant metric when evaluating the utility of database systems. Users care about
the time required to start an application on their phone. Biologists care about
how long their phylogenetic inference calculations take.
Writing efficient programs is not easy. The most straightforward solution is
often not the most efficient. Computationally efficient algorithms often employ
subtle tricks that can make them difficult to understand. Consequently, pro-
grammers often increase the conceptual complexity of a program in an effort to
reduce its computational complexity. To do this in a sensible way, we need to un-
derstand how to go about estimating the computational complexity of a program.
That is the topic of this chapter.

9.1 Thinking About Computational Complexity


How should one go about answering the question “How long will the following
function take to run?”
def f(i):
"""Assumes i is an int and i >= 0"""
answer = 1
while i >= 1:
answer *= i
i -= 1
return answer
136 INTRODUCTION TO COMPUTATION AND PROGRAMMING USING PYTHON

We could run the program on some input and time it. But that wouldn’t be par-
ticularly informative because the result would depend upon
• the speed of the computer on which it is run,
• the efficiency of the Python implementation on that machine, and
• the value of the input.
We get around the first two issues by using a more abstract measure of time.
Instead of measuring time in milliseconds, we measure time in terms of the
number of basic steps executed by the program.
For simplicity, we will use a random access machine as our model of compu-
tation. In a random access machine, steps are executed sequentially, one at a
time.53 A step is an operation that takes a fixed amount of time, such as binding a
variable to an object, making a comparison, executing an arithmetic operation,
or accessing an object in memory.
Now that we have a more abstract way to think about the meaning of time,
we turn to the question of dependence on the value of the input. We deal with
that by moving away from expressing time complexity as a single number and
instead relating it to the sizes of the inputs. This allows us to compare the effi-
ciency of two algorithms by talking about how the running time of each grows
with respect to the sizes of the inputs.
Of course, the actual running time of an algorithm depends not only upon
the sizes of the inputs but also upon their values. Consider, for example, the line-
ar search algorithm implemented by
def linearSearch(L, x):
for e in L:
if e == x:
return True
return False

Suppose that L is a list containing a million elements, and consider the call
linearSearch(L, 3). If the first element in L is 3, linearSearch will return True al-
most immediately. On the other hand, if 3 is not in L, linearSearch will have to
examine all one million elements before returning False.
In general, there are three broad cases to think about:
• The best-case running time is the running time of the algorithm when the in-
puts are as favorable as possible. I.e., the best-case running time is the mini-

53A more accurate model for today’s computers might be a parallel random access machine.
However, that adds considerable complexity to the algorithmic analysis, and often doesn’t make an
important qualitative difference in the answer.
CHAPTER 9. A SIMPLISTIC INTRODUCTION TO ALGORITHMIC COMPLEXITY 137

mum running time over all the possible inputs of a given size. For
linearSearch, the best-case running time is independent of the size of L.
• Similarly, the worst-case running time is the maximum running time over all
the possible inputs of a given size. For linearSearch, the worst-case running
time is linear in the size of L.
• By analogy with the definitions of the best-case and worst-case running time,
the average-case (also called expected-case) running time is the average run-
ning time over all possible inputs of a given size. Alternatively, if one has some
a priori information about the distribution of input values (e.g., that 90% of
the time x is in L), one can take that into account.
People usually focus on the worst case. All engineers share a common article
of faith, Murphy’s Law: If something can go wrong, it will go wrong. The worst-
case provides an upper bound on the running time. This is critical in situations
where there is a time constraint on how long a computation can take. It is not
good enough to know that “most of the time” the air traffic control system warns
of impending collisions before they occur.
Let’s look at the worst-case running time of an iterative implementation of
the factorial function:
def fact(n):
"""Assumes n is a natural number
Returns n!"""
answer = 1
while n > 1:
answer *= n
n -= 1
return answer

The number of steps required to run this program is something like 2 (1 for
the initial assignment statement and 1 for the return) + 5n (counting 1 step for
the test in the while, 2 steps for the first assignment statement in the while loop,
and 2 steps for the second assignment statement in the loop). So, for example, if n
is 1000, the function will execute roughly 5002 steps.
It should be immediately obvious that as n gets large, worrying about the dif-
ference between 5n and 5n+2 is kind of silly. For this reason, we typically ignore
additive constants when reasoning about running time. Multiplicative constants
are more problematical. Should we care whether the computation takes 1000
steps or 5000 steps? Multiplicative factors can be important. Whether a search
engine takes a half second or 2.5 seconds to service a query can be the difference
between whether people use that search engine or go to a competitor.
138 INTRODUCTION TO COMPUTATION AND PROGRAMMING USING PYTHON

On the other hand, when one is comparing two different algorithms, it is of-
ten the case that even multiplicative constants are irrelevant. Recall that in Chap-
ter 3 we looked at two algorithms, exhaustive enumeration and bisection search,
for finding an approximation to the square root of a floating point number.
Functions based on these algorithms are shown in Figure 9.1 and Figure 9.2.

def squareRootExhaustive(x, epsilon):


"""Assumes x and epsilon are positive floats & epsilon < 1
Returns a y such that y*y is within epsilon of x"""
step = epsilon**2
ans = 0.0
while abs(ans**2 - x) >= epsilon and ans*ans <= x:
ans += step
if ans*ans > x:
raise ValueError
return ans

Figure 9.1 Using exhaustive enumeration to approximate square root

def squareRootBi(x, epsilon):


"""Assumes x and epsilon are positive floats & epsilon < 1
Returns a y such that y*y is within epsilon of x"""
low = 0.0
high = max(1.0, x)
ans = (high + low)/2.0
while abs(ans**2 - x) >= epsilon:
if ans**2 < x:
low = ans
else:
high = ans
ans = (high + low)/2.0
return ans

Figure 9.2 Using bisection search to approximate square root

We saw that exhaustive enumeration was so slow as to be impractical for


many combinations of x and epsilon. For example, evaluating squareRootExhaus-
tive(100, 0.0001) requires roughly one billion iterations of the while loop. In
contrast, evaluating squareRootBi(100, 0.0001) takes roughly twenty iterations of
a slightly more complex while loop. When the difference in the number of itera-
tions is this large, it doesn’t really matter how many instructions are in the loop.
I.e., the multiplicative constants are irrelevant.
20 Chapter 1   Introduction to Computers and Programming

Checkpoint
1.18 A CPU understands instructions that are written only in what language?
1.19 A program has to be copied into what type of memory each time the CPU
executes it?
1.20 When a CPU executes the instructions in a program, it is engaged in what process?
1.21 What is assembly language?
1.22 What type of programming language allows you to create powerful and complex
programs without knowing how the CPU works?
1.23 Each language has a set of rules that must be strictly followed when writing a
program. What is this set of rules called?
1.24 What do you call a program that translates a high-level language program into a
separate machine language program?
1.25 What do you call a program that both translates and executes the instructions in
a high-level language program?
1.26 What type of mistake is usually caused by a misspelled keyword, a missing
punctuation character, or the incorrect use of an operator?

1.5 Using Python

CONCEPT: The Python interpreter can run Python programs that are saved in files
or interactively execute Python statements that are typed at the keyboard.
Python comes with a program named IDLE that simplifies the process of
writing, executing, and testing programs.

Installing Python
Before you can try any of the programs shown in this book, or write any programs of your
own, you need to make sure that Python is installed on your computer and properly con-
figured. If you are working in a computer lab, this has probably been done already. If you
are using your own computer, you can follow the instructions in Appendix A to download
and install Python.

The Python Interpreter


You learned earlier that Python is an interpreted language. When you install the Python
language on your computer, one of the items that is installed is the Python interpreter. The
Python interpreter is a program that can read Python programming statements and execute
them. (Sometimes, we will refer to the Python interpreter simply as the interpreter.)
You can use the interpreter in two modes: interactive mode and script mode. In interac-
tive mode, the interpreter waits for you to type Python statements on the keyboard. Once
you type a statement, the interpreter executes it and then waits for you to type another
statement. In script mode, the interpreter reads the contents of a file that contains Python
statements. Such a file is known as a Python program or a Python script. The interpreter
executes each statement in the Python program as it reads it.
1.5 Using Python 21

Interactive Mode
Once Python has been installed and set up on your system, you start the interpreter in
interactive mode by going to the operating system’s command line and typing the follow-
ing command:
python

If you are using Windows, you can alternatively type python in the Windows search box.
In the search results, you will see a program named something like Python 3.5. (The ­“3.5”
is the version of Python that is installed. At the time this is being written, Python 3.5 is
the latest version.) Clicking this item will start the Python interpreter in interactive mode.

NOTE: When the Python interpreter is running in interactive mode, it is commonly


called the Python shell.

When the Python interpreter starts in interactive mode, you will see something like the fol-
lowing displayed in a console window:
Python 3.7.4 (tags/v3.7.4:e09359112e, Jul 8 2019, 19:29:22)
[MSC v.1916 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license"
for more information.
>>>

The >>> that you see is a prompt that indicates the interpreter is waiting for you to type
a Python statement. Let’s try it out. One of the simplest things that you can do in Python
is print a message on the screen. For example, the following statement prints the message
Python programming is fun! on the screen:
print('Python programming is fun!')

You can think of this as a command that you are sending to the Python interpreter. If
you type the statement exactly as it is shown, the message Python programming is fun! is
printed on the screen. Here is an example of how you type this statement at the interpreter’s
prompt:
>>> print('Python programming is fun!') Enter

After typing the statement, you press the Enter key, and the Python interpreter executes the
statement, as shown here:
>>> print('Python programming is fun!') Enter
Python programming is fun!
>>>
22 Chapter 1   Introduction to Computers and Programming

After the message is displayed, the >>> prompt appears again, indicating the interpreter is
waiting for you to enter another statement. Let’s look at another example. In the following
sample session, we have entered two statements:
>>> print('To be or not to be') Enter
To be or not to be
>>> print('That is the question.') Enter
That is the question.
>>>

If you incorrectly type a statement in interactive mode, the interpreter will display an error
message. This will make interactive mode useful to you while you learn Python. As you
learn new parts of the Python language, you can try them out in interactive mode and get
immediate feedback from the interpreter.
To quit the Python interpreter in interactive mode on a Windows computer, press Ctrl-Z
(pressing both keys together) followed by Enter. On a Mac, Linux, or UNIX computer,
press Ctrl-D.

NOTE: In Chapter 2, we will discuss the details of statements like the ones previously
shown. If you want to try them now in interactive mode, make sure you type them exactly
as shown.

Writing Python Programs and Running


Them in Script Mode
Although interactive mode is useful for testing code, the statements that you enter in interac-
tive mode are not saved as a program. They are simply executed and their results displayed on
the screen. If you want to save a set of Python statements as a program, you save those state-
ments in a file. Then, to execute the program, you use the Python interpreter in script mode.
For example, suppose you want to write a Python program that displays the following three
lines of text:
Nudge nudge
Wink wink
Know what I mean?

To write the program you would use a simple text editor like Notepad (which is installed
on all Windows computers) to create a file containing the following statements:
print('Nudge nudge')
print('Wink wink')
print('Know what I mean?')

NOTE : It is possible to use a word processor to create a Python program, but you must
be sure to save the program as a plain text file. Otherwise, the Python interpreter will
not be able to read its contents.
1.5 Using Python 23

When you save a Python program, you give it a name that ends with the .py extension,
which identifies it as a Python program. For example, you might save the program previ-
ously shown with the name test.py. To run the program, you would go to the directory
in which the file is saved and type the following command at the operating system com-
mand line:
python test.py

This starts the Python interpreter in script mode and causes it to execute the statements in
the file test.py. When the program finishes executing, the Python interpreter exits.

The IDLE Programming Environment


The previous sections described how the Python interpreter can be started in interactive
mode or script mode at the operating system command line. As an alternative, you can use
VideoNote
an integrated development environment, which is a single program that gives you all of the
Using
Interactive tools you need to write, execute, and test a program.
Mode in IDLE
Recent versions of Python include a program named IDLE, which is automatically
installed when the Python language is installed. (IDLE stands for Integrated DeveLopment
Environment.) When you run IDLE, the window shown in Figure 1-20 appears. Notice
the >>> prompt appears in the IDLE window, indicating that the interpreter is running in
interactive mode. You can type Python statements at this prompt and see them executed in
the IDLE window.
IDLE also has a built-in text editor with features specifically designed to help you write
Python programs. For example, the IDLE editor “colorizes” code so keywords and other

Figure 1-20 IDLE


24 Chapter 1   Introduction to Computers and Programming

parts of a program are displayed in their own distinct colors. This helps make programs
easier to read. In IDLE, you can write programs, save them to disk, and execute them.
Appendix B provides a quick introduction to IDLE and leads you through the process­
of creating, saving, and executing a Python program.

NOTE: Although IDLE is installed with Python, there are several other Python IDEs
available. Your instructor might prefer that you use a specific one in class.

Review Questions
Multiple Choice
1. A(n) __________ is a set of instructions that a computer follows to perform a task.
a. compiler
b. program
c. interpreter
d. programming language
2. The physical devices that a computer is made of are referred to as __________.
a. hardware
b. software
c. the operating system
d. tools
3. The part of a computer that runs programs is called __________.
a. RAM
b. secondary storage
c. main memory
e. the CPU
4. Today, CPUs are small chips known as __________.
a. ENIACs
b. microprocessors
c. memory chips
d. operating systems
5. The computer stores a program while the program is running, as well as the data that
the program is working with, in __________.
a. secondary storage
b. the CPU
c. main memory
d. the microprocessor
6. This is a volatile type of memory that is used only for temporary storage while a pro-
gram is running.
a. RAM
b. secondary storage
c. the disk drive
d. the USB drive
CHAPTER

7 Lists and Tuples

TOPICS
7.1 Sequences 7.6 Copying Lists
7.2 Introduction to Lists 7.7 Processing Lists
7.3 List Slicing 7.8 List Comprehensions
7.4 Finding Items in Lists with the 7.9 Two-Dimensional Lists
in Operator 7.10 Tuples
7.5 List Methods and Useful Built-in 7.11 Plotting List Data with the
Functions ­matplotlib Package

7.1 Sequences

CONCEPT: A sequence is an object that holds multiple items of data, stored one
after the other. You can perform operations on a sequence to examine
and manipulate the items stored in it.
A sequence is an object that contains multiple items of data. The items that are in a sequence
are stored one after the other. Python provides various ways to perform operations on the
items that are stored in a sequence.
There are several different types of sequence objects in Python. In this chapter, we will
look at two of the fundamental sequence types: lists and tuples. Both lists and tuples are
sequences that can hold various types of data. The difference between lists and tuples is
simple: a list is mutable, which means that a program can change its contents, but a tuple
is immutable, which means that once it is created, its contents cannot be changed. We will
explore some of the operations that you may perform on these sequences, including ways
to access and manipulate their contents.

7.2 Introduction to Lists

CONCEPT: A list is an object that contains multiple data items. Lists are mutable,
which means that their contents can be changed during a program’s
execution. Lists are dynamic data structures, meaning that items may be
added to them or removed from them. You can use indexing, slicing, and
various methods to work with lists in a program.
361
362 Chapter 7   Lists and Tuples

A list is an object that contains multiple data items. Each item that is stored in a list is called
an element. Here is a statement that creates a list of integers:
even_numbers = [2, 4, 6, 8, 10]

The items that are enclosed in brackets and separated by commas are the list elements.
After this statement executes, the variable even_numbers will reference the list, as shown
in Figure 7-1.

Figure 7-1 A list of integers

even_numbers 2 4 6 8 10

The following is another example:


names = ['Molly', 'Steven', 'Will', 'Alicia', 'Adriana']

This statement creates a list of five strings. After the statement executes, the name variable
will reference the list as shown in Figure 7-2.

Figure 7-2 A list of strings

names Molly Steven Will Alicia Adriana

A list can hold items of different types, as shown in the following example:
info = ['Alicia', 27, 1550.87]

This statement creates a list containing a string, an integer, and a floating-point number.
After the statement executes, the info variable will reference the list as shown in Figure 7-3.

Figure 7-3 A list holding different types

info Alicia 27 1550.87

You can use the print function to display an entire list, as shown here:
numbers = [5, 10, 15, 20]
print(numbers)

In this example, the print function will display the elements of the list like this:
[5, 10, 15, 20]

Python also has a built-in list() function that can convert certain types of objects to lists.
For example, recall from Chapter 4 that the range function returns an iterable, which is an
object that holds a series of values that can be iterated over. You can use a statement such
as the following to convert the range function’s iterable object to a list:
numbers = list(range(5))
7.2 Introduction to Lists 363

When this statement executes, the following things happen:


• The range function is called with 5 passed as an argument. The function returns an
iterable containing the values 0, 1, 2, 3, 4.
• The iterable is passed as an argument to the list() function. The list() function
returns the list [0, 1, 2, 3, 4].
• The list [0, 1, 2, 3, 4] is assigned to the numbers variable.
Here is another example:
numbers = list(range(1, 10, 2))

Recall from Chapter 4 that when you pass three arguments to the range function, the first
argument is the starting value, the second argument is the ending limit, and the third argu-
ment is the step value. This statement will assign the list [1, 3, 5, 7, 9] to the numbers
variable.

The Repetition Operator


You learned in Chapter 2 that the * symbol multiplies two numbers. However, when the
operand on the left side of the * symbol is a sequence (such as a list) and the operand on
the right side is an integer, it becomes the repetition operator. The repetition operator makes
multiple copies of a list and joins them all together. Here is the general format:
list * n

In the general format, list is a list, and n is the number of copies to make. The following
interactive session demonstrates:
1 >>> numbers = [0] * 5 Enter
2 >>> print(numbers) Enter
3 [0, 0, 0, 0, 0]
4 >>>

Let’s take a closer look at each statement:


• In line 1, the expression [0] * 5 makes five copies of the list [0] and joins them all
together in a single list. The resulting list is assigned to the numbers variable.
• In line 2, the numbers variable is passed to the print function. The function’s output
is shown in line 3.
Here is another interactive mode demonstration:
1 >>> numbers = [1, 2, 3] * 3 Enter
2 >>> print(numbers) Enter
3 [1, 2, 3, 1, 2, 3, 1, 2, 3]
4 >>>

NOTE: Most programming languages allow you to create sequence structures known
as arrays, which are similar to lists, but are much more limited in their capabilities.
You cannot create traditional arrays in Python because lists serve the same purpose and
provide many more built-in capabilities.
364 Chapter 7   Lists and Tuples

Iterating over a List with the for Loop


One of the easiest ways to access the individual elements in a list is to use the for loop.
Here is the general format:
for variable in list:
statement
statement
etc.

In the general format, variable is the name of a variable, and list is the name of a list.
Each time the loop iterates, variable will reference a copy of an element in list, begin-
ning with the first element. We say that the loop iterates over the elements in the list. Here
is an example:
numbers = [1, 2, 3, 4]
for num in numbers:
print(num)

The numbers variable references a list with four elements, so this loop will iterate four
times. The first time the loop iterates, the num variable will reference the value 1, the sec-
ond time the loop iterates, the num variable will reference the value 2, and so forth. This is
illustrated in Figure 7-4. When the code executes, it will display the following:
1

Figure 7-4 Iterating over the list [1, 2, 3, 4]

1st Iteration for num in numbers: 2nd Iteration for num in numbers:
print(num) print(num)

numbers 1, 2, 3, 4 numbers 1, 2, 3, 4

num 1 num 2

3rd Iteration for num in numbers: 4th Iteration for num in numbers:
print(num) print(num)

numbers 1, 2, 3, 4 numbers 1, 2, 3, 4

num 3 num 4
7.2 Introduction to Lists 365

Figure 7-4 illustrates how the num variable references a copy of an element from the num-
bers list as the loop iterates. It is important to realize that we cannot use the num variable
to change the contents of an element in the list. If we change the value that num references
in the loop, it has no effect on the list. To demonstrate, look at the following code:
1 numbers = [1, 2, 3, 4]
2 for num in numbers:
3 num = 99
4 print(numbers)

The statement in line 3 merely reassigns the num variable to the value 99 each time the loop
iterates. It has no effect on the list that is referenced by numbers. When this code executes,
the statement in line 4 will print:
[1, 2, 3, 4]

Indexing
Another way that you can access the individual elements in a list is with an index. Each
element in a list has an index that specifies its position in the list. Indexing starts at 0, so the
index of the first element is 0, the index of the second element is 1, and so forth. The index
of the last element in a list is 1 less than the number of elements in the list.
For example, the following statement creates a list with 4 elements:
my_list = [10, 20, 30, 40]

The indexes of the elements in this list are 0, 1, 2, and 3. We can print the elements of the
list with the following statement:
print(my_list[0], my_list[1], my_list[2], my_list[3])

The following loop also prints the elements of the list:


index = 0
while index < 4:
print(my_list[index])
index += 1

You can also use negative indexes with lists to identify element positions relative to the end
of the list. The Python interpreter adds negative indexes to the length of the list to determine
the element position. The index −1 identifies the last element in a list, −2 identifies the next
to last element, and so forth. The following code shows an example:
my_list = [10, 20, 30, 40]
print(my_list[−1], my_list[−2], my_list[−3], my_list[−4])

In this example, the print function will display:


40 30 20 10

An IndexError exception will be raised if you use an invalid index with a list. For example,
look at the following code:
# This code will cause an IndexError exception.
my_list = [10, 20, 30, 40]
366 Chapter 7   Lists and Tuples

index = 0
while index < 5:
print(my_list[index])
index += 1

The last time that this loop begins an iteration, the index variable will be assigned the
value 4, which is an invalid index for the list. As a result, the statement that calls the print
function will cause an IndexError exception to be raised.

The len Function


Python has a built-in function named len that returns the length of a sequence, such as a
list. The following code demonstrates:
my_list = [10, 20, 30, 40]
size = len(my_list)

The first statement assigns the list [10, 20, 30, 40] to the my_list variable. The second
statement calls the len function, passing the my_list variable as an argument.
The function returns the value 4, which is the number of elements in the list. This value is
assigned to the size variable.
The len function can be used to prevent an IndexError exception when iterating over a
list with a loop. Here is an example:
my_list = [10, 20, 30, 40]
index = 0
while index < len(my_list):
print(my_list[index])
index += 1

Using a for Loop to Iterate by Index Over a List


You can use the len function along with the range function to get the indexes for a list.
For example, suppose we have the following list of strings:
names = ['Jenny', 'Kelly', 'Chloe', 'Aubrey']

The expression range(len(names)) will give us the values 0, 1, 2, and 3. Because these
values are the valid indexes for the list, we can use the expression in a for loop, as shown
in the following code:
1 names = ['Jenny', 'Kelly', 'Chloe', 'Aubrey']
2 for index in range(len(names)):
3 print(names[index])

As the for loop iterates, the index variable will be assigned the values 0, 1, 2, and 3. The
code will display the following:
Jenny
Kelly
Chloe
Aubrey
7.2 Introduction to Lists 367

Lists Are Mutable


Lists in Python are mutable, which means their elements can be changed. Consequently, an
expression in the form list[index] can appear on the left side of an assignment operator.
The following code shows an example:
1 numbers = [1, 2, 3, 4, 5]
2 print(numbers)
3 numbers[0] = 99
4 print(numbers)

The statement in line 2 will display


[1, 2, 3, 4, 5]

The statement in line 3 assigns 99 to numbers[0]. This changes the first value in the list to
99. When the statement in line 4 executes, it will display
[99, 2, 3, 4, 5]

When you use an indexing expression to assign a value to a list element, you must use a
valid index for an existing element or an IndexError exception will occur. For example,
look at the following code:
numbers = [1, 2, 3, 4, 5] # Create a list with 5 elements.
numbers[5] = 99 # This raises an exception!

The numbers list that is created in the first statement has five elements, with the indexes 0
through 4. The second statement will raise an IndexError exception because the numbers
list has no element at index 5.
If you want to use indexing expressions to fill a list with values, you have to create the list
first, as shown here:
1 # Create a list with 5 elements.
2 numbers = [0] * 5
3
4 # Fill the list with the value 99.
5 for index in range(len(numbers)):
6 numbers[index] = 99

The statement in line 2 creates a list with five elements, each element assigned the value 0.
The loop in lines 5 through 6 then steps through the list elements, assigning 99 to each one.
Program 7-1 shows an example of how user input can be assigned to the elements of a list.
This program gets sales amounts from the user and assigns them to a list.

Program 7-1 (sales_list.py)

1 # The NUM_DAYS constant holds the number of


2 # days that we will gather sales data for.
3 NUM_DAYS = 5
4
(program continues)
368 Chapter 7   Lists and Tuples

Program 7-1 (continued)

5 def main():
6 # Create a list to hold the sales for each day.
7 sales = [0] * NUM_DAYS
8
9 print('Enter the sales for each day.')
10
11 # Get the sales for each day.
12 for index in range(len(sales)):
13 sales[index] = float(input(f'Day #{index + 1}: '))
14
15 # Display the values entered.
16 print('Here are the values you entered:')
17 for value in sales:
18 print(value)
19
20 # Call the main function.
21 if _ _name_ _ == '_ _main_ _':
22 main()

Program Output (with input shown in bold)


Enter the sales for each day.
Day #1: 1000 Enter
Day #2: 2000 Enter
Day #3: 3000 Enter
Day #4: 4000 Enter
Day #5: 5000 Enter
Here are the values you entered:
1000.0
2000.0
3000.0
4000.0
5000.0

The statement in line 3 creates the variable NUM_DAYS, which is used as a constant for the
number of days. The statement in line 7 creates a list with five elements, with each element
assigned the value 0.
The loop in lines 12 through 13 iterates 5 times. The first time it iterates, index references
the value 0, so the statement in line 13 assigns the user’s input to sales[0]. The second
time the loop iterates, index references the value 1, so the statement in line 13 assigns the
user’s input to sales[1]. This continues until input values have been assigned to all the
elements in the list.
7.2 Introduction to Lists 369

Concatenating Lists
To concatenate means to join two things together. You can use the + operator to concat-
enate two lists. Here is an example:
list1 = [1, 2, 3, 4]
list2 = [5, 6, 7, 8]
list3 = list1 + list2

After this code executes, list1 and list2 remain unchanged, and list3 references the
following list:
[1, 2, 3, 4, 5, 6, 7, 8]

The following interactive mode session also demonstrates list concatenation:


>>> girl_names = ['Joanne', 'Karen', 'Lori'] Enter
>>> boy_names = ['Chris', 'Jerry', 'Will'] Enter
>>> all_names = girl_names + boy_names Enter
>>> print(all_names) Enter
['Joanne', 'Karen', 'Lori', 'Chris', 'Jerry', 'Will']

You can also use the += augmented assignment operator to concatenate one list to another.
Here is an example:
list1 = [1, 2, 3, 4]
list2 = [5, 6, 7, 8]
list1 += list2

The last statement appends list2 to list1. After this code executes, list2 remains
unchanged, but list1 references the following list:
[1, 2, 3, 4, 5, 6, 7, 8]

The following interactive mode session also demonstrates the += operator used for list
concatenation:
>>> girl_names = ['Joanne', 'Karen', 'Lori'] Enter
>>> girl_names += ['Jenny', 'Kelly'] Enter
>>> print(girl_names) Enter
['Joanne', 'Karen', 'Lori', 'Jenny', 'Kelly']
>>>

NOTE: Keep in mind that you can concatenate lists only with other lists. If you try to
concatenate a list with something that is not a list, an exception will be raised.

Checkpoint
7.1 What will the following code display?
numbers = [1, 2, 3, 4, 5]
numbers[2] = 99
print(numbers)
370 Chapter 7   Lists and Tuples

7.2 What will the following code display?


numbers = list(range(3))
print(numbers)
7.3 What will the following code display?
numbers = [10] * 5
print(numbers)
7.4 What will the following code display?
numbers = list(range(1, 10, 2))
for n in numbers:
print(n)
7.5 What will the following code display?
numbers = [1, 2, 3, 4, 5]
print(numbers[−2])
7.6 How do you find the number of elements in a list?
7.7 What will the following code display?
numbers1 = [1, 2, 3]
numbers2 = [10, 20, 30]
numbers3 = numbers1 + numbers2
print(numbers1)
print(numbers2)
print(numbers3)
7.8 What will the following code display?
numbers1 = [1, 2, 3]
numbers2 = [10, 20, 30]
numbers2 += numbers1
print(numbers1)
print(numbers2)

7.3 List Slicing

CONCEPT: A slicing expression selects a range of elements from a sequence.

You have seen how indexing allows you to select a specific element in a sequence. Sometimes
you want to select more than one element from a sequence. In Python, you can write expres-
sions that select subsections of a sequence, known as slices.
A slice is a span of items that are taken from a sequence. When you take a slice from a list,
you get a span of elements from within the list. To get a slice of a list, you write an expres-
VideoNote
List Slicing sion in the following general format:
list_name[start : end]

In the general format, start is the index of the first element in the slice, and end is the
index marking the end of the slice. The expression returns a list containing a copy of
7.3 List Slicing 371

the elements from start up to (but not including) end. For example, suppose we create
the following list:
days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
  
'Thursday', 'Friday', 'Saturday']

The following statement uses a slicing expression to get the elements from indexes 2 up to,
but not including, 5:
mid_days = days[2:5]

After this statement executes, the mid_days variable references the following list:
['Tuesday', 'Wednesday', 'Thursday']

You can quickly use the interactive mode interpreter to see how slicing works. For example,
look at the following session. (We have added line numbers for easier reference.)
1 >>> numbers = [1, 2, 3, 4, 5] Enter
2 >>> print(numbers) Enter
3 [1, 2, 3, 4, 5]
4 >>> print(numbers[1:3]) Enter
5 [2, 3]
6 >>>

Here is a summary of each line:


• In line 1, we created the list and [1, 2, 3, 4, 5] and assigned it to the numbers
variable.
• In line 2, we passed numbers as an argument to the print function. The print func-
tion displayed the list in line 3.
• In line 4, we sent the slice numbers[1:3] as an argument to the print function. The
print function displayed the slice in line 5.

If you leave out the start index in a slicing expression, Python uses 0 as the starting index.
The following interactive mode session shows an example:
1 >>> numbers = [1, 2, 3, 4, 5] Enter
2 >>> print(numbers) Enter
3 [1, 2, 3, 4, 5]
4 >>> print(numbers[:3]) Enter
5 [1, 2, 3]
6 >>>

Notice line 4 sends the slice numbers[:3] as an argument to the print function. Because
the starting index was omitted, the slice contains the elements from index 0 up to 3.
If you leave out the end index in a slicing expression, Python uses the length of the list as
the end index. The following interactive mode session shows an example:
1 >>> numbers = [1, 2, 3, 4, 5] Enter
2 >>> print(numbers) Enter
3 [1, 2, 3, 4, 5]
4 >>> print(numbers[2:]) Enter
5 [3, 4, 5]
6 >>>
372 Chapter 7   Lists and Tuples

Notice line 4 sends the slice numbers[2:] as an argument to the print function. Because
the ending index was omitted, the slice contains the elements from index 2 through the end
of the list.
If you leave out both the start and end index in a slicing expression, you get a copy of the
entire list. The following interactive mode session shows an example:
1 >>> numbers = [1, 2, 3, 4, 5] Enter
2 >>> print(numbers) Enter
3 [1, 2, 3, 4, 5]
4 >>> print(numbers[:]) Enter
5 [1, 2, 3, 4, 5]
6 >>>

The slicing examples we have seen so far get slices of consecutive elements from lists. Slicing
expressions can also have step value, which can cause elements to be skipped in the list.
The following interactive mode session shows an example of a slicing expression with a
step value:
1 >>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Enter
2 >>> print(numbers) Enter
3 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
4 >>> print(numbers[1:8:2]) Enter
5 [2, 4, 6, 8]
6 >>>

In the slicing expression in line 4, the third number inside the brackets is the step value. A
step value of 2, as used in this example, causes the slice to contain every second element
from the specified range in the list.
You can also use negative numbers as indexes in slicing expressions to reference positions
relative to the end of the list. Python adds a negative index to the length of a list to get
the position referenced by that index. The following interactive mode session shows an
example:
1 >>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Enter
2 >>> print(numbers) Enter
3 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
4 >>> print(numbers[−5:]) Enter
5 [6, 7, 8, 9, 10]
6 >>>

NOTE: Invalid indexes do not cause slicing expressions to raise an exception. For
example:
• If the end index specifies a position beyond the end of the list, Python will use the
length of the list instead.
• If the start index specifies a position before the beginning of the list, Python will
use 0 instead.
• If the start index is greater than the end index, the slicing expression will return
an empty list.
7.4 Finding Items in Lists with the in Operator 373

Checkpoint
7.9 What will the following code display?
numbers = [1, 2, 3, 4, 5]
my_list = numbers[1:3]
print(my_list)
7.10 What will the following code display?
numbers = [1, 2, 3, 4, 5]
my_list = numbers[1:]
print(my_list)
7.11 What will the following code display?
numbers = [1, 2, 3, 4, 5]
my_list = numbers[:1]
print(my_list)
7.12 What will the following code display?
numbers = [1, 2, 3, 4, 5]
my_list = numbers[:]
print(my_list)
7.13 What will the following code display?
numbers = [1, 2, 3, 4, 5]
my_list = numbers[−3:]
print(my_list)

7.4 Finding Items in Lists with the in Operator

CONCEPT: You can search for an item in a list using the in operator.

In Python, you can use the in operator to determine whether an item is contained in a list.
Here is the general format of an expression written with the in operator to search for an
item in a list:
item in list

In the general format, item is the item for which you are searching, and list is a list. The
expression returns true if item is found in the list, or false otherwise. Program 7-2 shows
an example.

Program 7-2 (in_list.py)

1 # This program demonstrates the in operator


2 # used with a list.
3
4 def main():
(program continues)
374 Chapter 7   Lists and Tuples

Program 7-2 (continued)

5 # Create a list of product numbers.


6 prod_nums = ['V475', 'F987', 'Q143', 'R688']
7
8 # Get a product number to search for.
9 search = input('Enter a product number: ')
10
11 # Determine whether the product number is in the list.
12 if search in prod_nums:
13 print(f'{search} was found in the list.')
14 else:
15 print(f'{search} was not found in the list.')
16
17 # Call the main function.
18 if _ _name_ _ == '_ _main_ _':
19 main()

Program Output (with input shown in bold)


Enter a product number: Q143 Enter
Q143 was found in the list.

Program Output (with input shown in bold)


Enter a product number: B000 Enter
B000 was not found in the list.

The program gets a product number from the user in line 9 and assigns it to the search
variable. The if statement in line 12 determines whether search is in the prod_nums list.
You can use the not in operator to determine whether an item is not in a list. Here is an
example:
if search not in prod_nums:
print(f'{search} was not found in the list.')
else:
print(f'{search} was found in the list.')

Checkpoint
7.14 What will the following code display?
names = ['Jim', 'Jill', 'John', 'Jasmine']
if 'Jasmine' not in names:
print('Cannot find Jasmine.')
else:
print("Jasmine's family:")
print(names)
7.5 List Methods and Useful Built-in Functions 375

7.5 List Methods and Useful Built-in Functions

CONCEPT: Lists have numerous methods that allow you to work with the elements
that they contain. Python also provides some built-in functions that are
useful for working with lists.

Lists have numerous methods that allow you to add elements, remove elements, change
the ordering of elements, and so forth. We will look at a few of these methods,1 which are
listed in Table 7-1.

Table 7-1 A few of the list methods


Method Description
append(item) Adds item to the end of the list.
index(item) Returns the index of the first element whose value is equal to item.
A ValueError exception is raised if item is not found in the list.
insert(index, item) Inserts item into the list at the specified index. When an item is
inserted into a list, the list is expanded in size to accommodate the new
item. The item that was previously at the specified index, and all the
items after it, are shifted by one position toward the end of the list.
No exceptions will occur if you specify an invalid index. If you spec-
ify an index beyond the end of the list, the item will be added to the
end of the list. If you use a negative index that specifies an invalid
position, the item will be inserted at the beginning of the list.
sort() Sorts the items in the list so they appear in ascending order (from
the lowest value to the highest value).
remove(item) Removes the first occurrence of item from the list. A ValueError
exception is raised if item is not found in the list.
reverse() Reverses the order of the items in the list.

The append Method


The append method is commonly used to add items to a list. The item that is passed as an
argument is appended to the end of the list’s existing elements. Program 7-3 shows an example.

Program 7-3 (list_append.py)

1 # This program demonstrates how the append


2 # method can be used to add items to a list.
3
4 def main():
5 # First, create an empty list.
6 name_list = [] (program continues)

1
We do not cover all of the list methods in this book. For a description of all of the list methods,
see the Python documentation at www.python.org.
376 Chapter 7   Lists and Tuples

Program 7-3 (continued)

7
8 # Create a variable to control the loop.
9 again = 'y'
10
11 # Add some names to the list.
12 while again == 'y':
13 # Get a name from the user.
14 name = input('Enter a name: ')
15
16 # Append the name to the list.
17 name_list.append(name)
18
19 # Add another one?
20 print('Do you want to add another name?')
21 again = input('y = yes, anything else = no: ')
22 print()
23
24 # Display the names that were entered.
25 print('Here are the names you entered.')
26
27 for name in name_list:
28 print(name)
29
30 # Call the main function.
31 if _ _name_ _ == '_ _main_ _':
32 main()

Program Output (with input shown in bold)


Enter a name: Kathryn Enter
Do you want to add another name?
y = yes, anything else = no: y Enter

Enter a name: Chris Enter


Do you want to add another name?
y = yes, anything else = no: y Enter

Enter a name: Kenny Enter


Do you want to add another name?
y = yes, anything else = no: y Enter

Enter a name: Renee Enter


Do you want to add another name?
y = yes, anything else = no: n Enter

Here are the names you entered.


Kathryn
Chris
Kenny
Renee
7.5 List Methods and Useful Built-in Functions 377

Notice the statement in line 6:


name_list = []

This statement creates an empty list (a list with no elements) and assigns it to the
name_list variable. Inside the loop, the append method is called to build the list. The first
time the method is called, the argument passed to it will become element 0. The second
time the method is called, the argument passed to it will become element 1. This continues
until the user exits the loop.

The index Method


Earlier, you saw how the in operator can be used to determine whether an item is in a list.
Sometimes you need to know not only whether an item is in a list, but where it is located.
The index method is useful in these cases. You pass an argument to the index method,
and it returns the index of the first element in the list containing that item. If the item is
not found in the list, the method raises a ValueError exception. Program 7-4 demonstrates
the index method.

Program 7-4 (index_list.py)

1 # This program demonstrates how to get the


2 # index of an item in a list and then replace
3 # that item with a new item.
4
5 def main():
6 # Create a list with some items.
7 food = ['Pizza', 'Burgers', 'Chips']
8
9 # Display the list.
10 print('Here are the items in the food list:')
11 print(food)
12
13 # Get the item to change.
14 item = input('Which item should I change? ')
15
16 try:
17 # Get the item's index in the list.
18 item_index = food.index(item)
19
20 # Get the value to replace it with.
21 new_item = input('Enter the new value: ')
22
23 # Replace the old item with the new item.
24 food[item_index] = new_item
25
26 # Display the list.
27 print('Here is the revised list:')
(program continues)
378 Chapter 7   Lists and Tuples

Program 7-4 (continued)

28 print(food)
29 except ValueError:
30 print('That item was not found in the list.')
31
32 # Call the main function.
33 if _ _name_ _ == '_ _main_ _':
34 main()

Program Output (with input shown in bold)


Here are the items in the food list:
['Pizza', 'Burgers', 'Chips']
Which item should I change? Burgers Enter
Enter the new value: Pickles Enter
Here is the revised list:
['Pizza', 'Pickles', 'Chips']

The elements of the food list are displayed in line 11, and in line 14, the user is asked which
item he or she wants to change. Line 18 calls the index method to get the index of the item.
Line 21 gets the new value from the user, and line 24 assigns the new value to the element
holding the old value.

The insert Method


The insert method allows you to insert an item into a list at a specific position. You
pass two arguments to the insert method: an index specifying where the item should be
inserted and the item that you want to insert. Program 7-5 shows an example.

Program 7-5 (insert_list.py)

1 # This program demonstrates the insert method.


2
3 def main():
4 # Create a list with some names.
5 names = ['James', 'Kathryn', 'Bill']
6
7 # Display the list.
8 print('The list before the insert:')
9 print(names)
10
11 # Insert a new name at element 0.
12 names.insert(0, 'Joe')
13
14 # Display the list again.
15 print('The list after the insert:')
7.5 List Methods and Useful Built-in Functions 379

16 print(names)
17
18 # Call the main function.
19 if _ _name_ _ == '_ _main_ _':
20 main()

Program Output
The list before the insert:
['James', 'Kathryn', 'Bill']
The list after the insert:
['Joe', 'James', 'Kathryn', 'Bill']

The sort Method


The sort method rearranges the elements of a list so they appear in ascending order (from
the lowest value to the highest value). Here is an example:
my_list = [9, 1, 0, 2, 8, 6, 7, 4, 5, 3]
print('Original order:', my_list)
my_list.sort()
print('Sorted order:', my_list)

When this code runs, it will display the following:


Original order: [9, 1, 0, 2, 8, 6, 7, 4, 5, 3]
Sorted order: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Here is another example:


my_list = ['beta', 'alpha', 'delta', 'gamma']
print('Original order:', my_list)
my_list.sort()
print('Sorted order:', my_list)

When this code runs, it will display the following:


Original order: ['beta', 'alpha', 'delta', 'gamma']
Sorted order: ['alpha', 'beta', 'delta', 'gamma']

The remove Method


The remove method removes an item from the list. You pass an item to the method as an
argument, and the first element containing that item is removed. This reduces the size of the
list by one element. All of the elements after the removed element are shifted one position
toward the beginning of the list. A ValueError exception is raised if the item is not found
in the list. Program 7-6 demonstrates the method.
380 Chapter 7   Lists and Tuples

Program 7-6 (remove_item.py)

1 # This program demonstrates how to use the remove


2 # method to remove an item from a list.
3
4 def main():
5 # Create a list with some items.
6 food = ['Pizza', 'Burgers', 'Chips']
7
8 # Display the list.
9 print('Here are the items in the food list:')
10 print(food)
11
12 # Get the item to change.
13 item = input('Which item should I remove? ')
14
15 try:
16 # Remove the item.
17 food.remove(item)
18
19 # Display the list.
20 print('Here is the revised list:')
21 print(food)
22
23 except ValueError:
24 print('That item was not found in the list.')
25
26 # Call the main function.
27 if _ _name_ _ == '_ _main_ _':
28 main()

Program Output (with input shown in bold)


Here are the items in the food list:
['Pizza', 'Burgers', 'Chips']
Which item should I remove? Burgers Enter
Here is the revised list:
['Pizza', 'Chips']

The reverse Method


The reverse method simply reverses the order of the items in the list. Here is an example:
my_list = [1, 2, 3, 4, 5]
print('Original order:', my_list)
my_list.reverse()
print('Reversed:', my_list)
7.5 List Methods and Useful Built-in Functions 381

This code will display the following:


Original order: [1, 2, 3, 4, 5]
Reversed: [5, 4, 3, 2, 1]

The del Statement


The remove method you saw earlier removes a specific item from a list, if that item is in the
list. Some situations might require you remove an element from a specific index, regardless
of the item that is stored at that index. This can be accomplished with the del statement.
Here is an example of how to use the del statement:
my_list = [1, 2, 3, 4, 5]
print('Before deletion:', my_list)
del my_list[2]
print('After deletion:', my_list)

This code will display the following:


Before deletion: [1, 2, 3, 4, 5]
After deletion: [1, 2, 4, 5]

The min and max Functions


Python has two built-in functions named min and max that work with sequences. The min
function accepts a sequence, such as a list, as an argument and returns the item that has the
lowest value in the sequence. Here is an example:
my_list = [5, 4, 3, 2, 50, 40, 30]
print('The lowest value is', min(my_list))

This code will display the following:


The lowest value is 2

The max function accepts a sequence, such as a list, as an argument and returns the item
that has the highest value in the sequence. Here is an example:
my_list = [5, 4, 3, 2, 50, 40, 30]
print('The highest value is', max(my_list))

This code will display the following:


The highest value is 50

Checkpoint
7.15 What is the difference between calling a list’s remove method and using the del
statement to remove an element?
7.16 How do you find the lowest and highest values in a list?
382 Chapter 7   Lists and Tuples

7.17 Assume the following statement appears in a program:


names = []
Which of the following statements would you use to add the string ‘Wendy’ to the
list at index 0? Why would you select this statement instead of the other?
a. names[0] = 'Wendy'
b. names.append('Wendy')
7.18 Describe the following list methods:
a. index
b. insert
c. sort
d. reverse

7.6 Copying Lists

CONCEPT: To make a copy of a list, you must copy the list’s elements.

Recall that in Python, assigning one variable to another variable simply makes both vari-
ables reference the same object in memory. For example, look at the following code:
# Create a list.
list1 = [1, 2, 3, 4]
# Assign the list to the list2 variable.
list2 = list1

After this code executes, both variables list1 and list2 will reference the same list in
memory. This is shown in Figure 7-5.

Figure 7-5 list1 and list2 reference the same list

list1

1 2 3 4

list2

To demonstrate this, look at the following interactive session:


1 >>> list1 = [1, 2, 3, 4] Enter
2 >>> list2 = list1 Enter
3 >>> print(list1) Enter
4 [1, 2, 3, 4]
5 >>> print(list2) Enter
6 [1, 2, 3, 4]
7 >>> list1[0] = 99 Enter
8 >>> print(list1) Enter
9 [99, 2, 3, 4]
10 >>> print(list2) Enter
11 [99, 2, 3, 4]
12 >>>
7.7 Processing Lists 383

Let’s take a closer look at each line:


• In line 1, we create a list of integers and assign the list to the list1 variable.
• In line 2, we assign list1 to list2. After this, both list1 and list2 reference the
same list in memory.
• In line 3, we print the list referenced by list1. The output of the print function is
shown in line 4.
• In line 5, we print the list referenced by list2. The output of the print function is
shown in line 6. Notice it is the same as the output shown in line 4.
• In line 7, we change the value of list[0] to 99.
• In line 8, we print the list referenced by list1. The output of the print function is
shown in line 9. Notice the first element is now 99.
• In line 10, we print the list referenced by list2. The output of the print function is
shown in line 11. Notice the first element is 99.
In this interactive session, the list1 and list2 variables reference the same list in memory.
Suppose you wish to make a copy of the list, so list1 and list2 reference two separate
but identical lists. One way to do this is with a loop that copies each element of the list.
Here is an example:
# Create a list with values.
list1 = [1, 2, 3, 4]
# Create an empty list.
list2 = []
# Copy the elements of list1 to list2.
for item in list1:
list2.append(item)

After this code executes, list1 and list2 will reference two separate but identical lists.
A simpler and more elegant way to accomplish the same task is to use the concatenation
operator, as shown here:
# Create a list with values.
list1 = [1, 2, 3, 4]
# Create a copy of list1.
list2 = [] + list1

The last statement in this code concatenates an empty list with list1 and assigns the
resulting list to list2. As a result, list1 and list2 will reference two separate but
identical lists.

7.7 Processing Lists


So far, you’ve learned a wide variety of techniques for working with lists. Now we will
look at a number of ways that programs can process the data held in a list. For example,
the following In the Spotlight section shows how list elements can be used in calculations.
384 Chapter 7   Lists and Tuples

In the Spotlight:
Using List Elements in a Math Expression
Megan owns a small neighborhood coffee shop, and she has six employees who work as
baristas (coffee bartenders). All of the employees have the same hourly pay rate. Megan has
asked you to design a program that will allow her to enter the number of hours worked by
each employee, then display the amounts of all the employees’ gross pay. You determine the
program should perform the following steps:
1. For each employee: get the number of hours worked and store it in a list element.
2. For each list element: use the value stored in the element to calculate an employee’s
gross pay. Display the amount of the gross pay.
Program 7-7 shows the code for the program.

Program 7-7 (barista_pay.py)

1 # This program calculates the gross pay for


2 # each of Megan's baristas.
3
4 # NUM_EMPLOYEES is used as a constant for the
5 # size of the list.
6 NUM_EMPLOYEES = 6
7
8 def main():
9 # Create a list to hold employee hours.
10 hours = [0] * NUM_EMPLOYEES
11
12 # Get each employee's hours worked.
13 for index in range(NUM_EMPLOYEES):
14 hours[index] = float(
15 input(f'Hours worked by employee {index + 1}: '))
16
17 # Get the hourly pay rate.
18 pay_rate = float(input('Hourly pay rate: '))
19
20 # Display each employee's gross pay.
21 for index in range(NUM_EMPLOYEES):
22 gross_pay = hours[index] * pay_rate
23 print(f'Gross pay for employee {index + 1}: ${gross_pay:,.2f}')
24
25 # Call the main function.
26 if _ _name_ _ == '_ _main_ _':
27 main()
7.7 Processing Lists 385

Program Output (with input shown in bold)


Hours worked by employee 1: 10 Enter
Hours worked by employee 2: 20 Enter
Hours worked by employee 3: 15 Enter
Hours worked by employee 4: 40 Enter
Hours worked by employee 5: 20 Enter
Hours worked by employee 6: 18 Enter
Hourly pay rate: 12.75 Enter
Gross pay for employee 1: $127.50
Gross pay for employee 2: $255.00
Gross pay for employee 3: $191.25
Gross pay for employee 4: $510.00
Gross pay for employee 5: $255.00
Gross pay for employee 6: $229.50

NOTE: Suppose Megan’s business increases and she hires two additional baristas.
This would require you to change the program so it processes eight employees instead
of six. Because you used a constant for the list size, this is a simple modification—you
just change the statement in line 6 to read:
NUM_EMPLOYEES = 8

Because the NUM_EMPLOYEES constant is used in line 10 to create the list, the size of the
hours list will automatically become eight. Also, because you used the NUM_EMPLOYEES
constant to control the loop iterations in lines 13 and 21, the loops will automatically
iterate eight times, once for each employee.
Imagine how much more difficult this modification would be if you had not used a
constant to determine the list size. You would have to change each individual statement
in the program that refers to the list size. Not only would this require more work, but it
would open the possibility for errors. If you overlooked any one of the statements that
refer to the list size, a bug would occur.

Totaling the Values in a List


Assuming a list contains numeric values, to calculate the total of those values, you use a
loop with an accumulator variable. The loop steps through the list, adding the value of each
element to the accumulator. Program 7-8 demonstrates the algorithm with a list named
numbers.
386 Chapter 7   Lists and Tuples

Program 7-8 (total_list.py)

1 # This program calculates the total of the values


2 # in a list.
3
4 def main():
5 # Create a list.
6 numbers = [2, 4, 6, 8, 10]
7
8 # Create a variable to use as an accumulator.
9 total = 0
10
11 # Calculate the total of the list elements.
12 for value in numbers:
13 total += value
14
15 # Display the total of the list elements.
16 print(f'The total of the elements is {total}.')
17
18 # Call the main function.
19 if _ _name_ _ == '_ _main_ _':
20 main()

Program Output
The total of the elements is 30.

Averaging the Values in a List


The first step in calculating the average of the values in a list is to get the total of the
values. You saw how to do that with a loop in the preceding section. The second step is
to divide the total by the number of elements in the list. Program 7-9 demonstrates the
algorithm.

Program 7-9 (average_list.py)

1 # This program calculates the average of the values


2 # in a list.
3
4 def main():
5 # Create a list.
6 scores = [2.5, 7.3, 6.5, 4.0, 5.2]
7
8 # Create a variable to use as an accumulator.
9 total = 0.0
10
11 # Calculate the total of the list elements.
7.7 Processing Lists 387

12 for value in scores:


13 total += value
14
15 # Calculate the average of the elements.
16 average = total / len(scores)
17
18 # Display the total of the list elements.
19 print(f'The average of the elements is {average}.')
20
21 # Call the main function.
23 if _ _name_ _ == '_ _main_ _':
24 main()

Program Output
The average of the elements is 5.3.

Passing a List as an Argument to a Function


Recall from Chapter 5 that as a program grows larger and more complex, it should be bro-
ken down into functions that each performs a specific task. This makes the program easier
to understand and to maintain.
You can easily pass a list as an argument to a function. This gives you the ability to put
many of the operations that you perform on a list in their own functions. When you need
to call these functions, you can pass the list as an argument.
Program 7-10 shows an example of a program that uses such a function. The function in
this program accepts a list as an argument and returns the total of the list’s elements.

Program 7-10 (total_function.py)

1 # This program uses a function to calculate the


2 # total of the values in a list.
3
4 def main():
5 # Create a list.
6 numbers = [2, 4, 6, 8, 10]
7
8 # Display the total of the list elements.
9 print(f'The total is {get_total(numbers)}.')
10
11 # The get_total function accepts a list as an
12 # argument returns the total of the values in
13 # the list.
14 def get_total(value_list):
15 # Create a variable to use as an accumulator.
16 total = 0
(program continues)
388 Chapter 7   Lists and Tuples

Program 7-10 (continued)

17
18 # Calculate the total of the list elements.
19 for num in value_list:
20 total += num
21
22 # Return the total.
23 return total
24
25 # Call the main function.
26 if _ _name_ _ == '_ _main_ _':
27 main()

Program Output
The total is 30.

Returning a List from a Function


A function can return a reference to a list. This gives you the ability to write a function
that creates a list and adds elements to it, then returns a reference to the list so other parts
of the program can work with it. The code in Program 7-11 shows an example. It uses a
function named get_values that gets a series of values from the user, stores them in a list,
then returns a reference to the list.

Program 7-11 (return_list.py)

1 # This program uses a function to create a list.


2 # The function returns a reference to the list.
3
4 def main():
5 # Get a list with values stored in it.
6 numbers = get_values()
7
8 # Display the values in the list.
9 print('The numbers in the list are:')
10 print(numbers)
11
12 # The get_values function gets a series of numbers
13 # from the user and stores them in a list. The
14 # function returns a reference to the list.
15 def get_values():
16 # Create an empty list.
17 values = []
7.7 Processing Lists 389

18
19 # Create a variable to control the loop.
20 again = 'y'
21
22 # Get values from the user and add them to
23 # the list.
24 while again == 'y':
25 # Get a number and add it to the list.
26 num = int(input('Enter a number: '))
27 values.append(num)
28
29 # Want to do this again?
30 print('Do you want to add another number?')
31 again = input('y = yes, anything else = no: ')
32 print()
33
34 # Return the list.
35 return values
36
37 # Call the main function.
38 if _ _name_ _ == '_ _main_ _':
39 main()

Program Output (with input shown in bold)


Enter a number: 1 Enter
Do you want to add another number?
y = yes, anything else = no: y Enter

Enter a number: 2 Enter


Do you want to add another number?
y = yes, anything else = no: y Enter

Enter a number: 3 Enter


Do you want to add another number?
y = yes, anything else = no: y Enter

Enter a number: 4 Enter


Do you want to add another number?
y = yes, anything else = no: y Enter

Enter a number: 5 Enter


Do you want to add another number?
y = yes, anything else = no: n Enter
The numbers in the list are:
[1, 2, 3, 4, 5]
390 Chapter 7   Lists and Tuples

In the Spotlight:
Processing a List
Dr. LaClaire gives a series of exams during the semester in her chemistry class. At the end of
the semester, she drops each student’s lowest test score before averaging the scores. She has
asked you to design a program that will read a student’s test scores as input and calculate
the average with the lowest score dropped. Here is the algorithm that you developed:
Get the student’s test scores.
Calculate the total of the scores.
Find the lowest score.
Subtract the lowest score from the total. This gives the adjusted total.
Divide the adjusted total by 1 less than the number of test scores. This is the average.
Display the average.
Program 7-12 shows the code for the program, which is divided into three functions. Rather
than presenting the entire program at once, let’s first examine the main function, then each
additional function separately. Here is the main function:

Program 7-12 drop_lowest_score.py: main function

1 # This program gets a series of test scores and


2 # calculates the average of the scores with the
3 # lowest score dropped.
4
5 def main():
6 # Get the test scores from the user.
7 scores = get_scores()
8
9 # Get the total of the test scores.
10 total = get_total(scores)
11
12 # Get the lowest test score.
13 lowest = min(scores)
14
15 # Subtract the lowest score from the total.
16 total −= lowest
17
18 # Calculate the average. Note that we divide
19 # by 1 less than the number of scores because
20 # the lowest score was dropped.
21 average = total / (len(scores) − 1)
22
23 # Display the average.
24 print(f'Average with lowest score dropped: {average}')
25
7.7 Processing Lists 391

Line 7 calls the get_scores function. The function gets the test scores from the user and
returns a reference to a list containing those scores. The list is assigned to the scores variable.
Line 10 calls the get_total function, passing the scores list as an argument. The function
returns the total of the values in the list. This value is assigned to the total variable.
Line 13 calls the built-in min function, passing the scores list as an argument. The function
returns the lowest value in the list. This value is assigned to the lowest variable.
Line 16 subtracts the lowest test score from the total variable. Then, line 21 calculates the
average by dividing total by len(scores) 2 1. (The program divides by len (scores) 2 1
because the lowest test score was dropped.) Lines 24 and 25 display the average.
Next is the get_scores function.

Program 7-12 drop_lowest_score.py: get_scores function

26 # The get_scores function gets a series of test


27 # scores from the user and stores them in a list.
28 # A reference to the list is returned.
29 def get_scores():
30 # Create an empty list.
31 test_scores = []
32
33 # Create a variable to control the loop.
34 again = 'y'
35
36 # Get the scores from the user and add them to
37 # the list.
38 while again == 'y':
39 # Get a score and add it to the list.
40 value = float(input('Enter a test score: '))
41 test_scores.append(value)
42
43 # Want to do this again?
44 print('Do you want to add another score?')
45 again = input('y = yes, anything else = no: ')
46 print()
47
48 # Return the list.
49 return test_scores
50

The get_scores function prompts the user to enter a series of test scores. As each score
is entered, it is appended to a list. The list is returned in line 50. Next is the get_total
function.
392 Chapter 7   Lists and Tuples

Program 7-12 drop_lowest_score.py: get_total function


51 # The get_total function accepts a list as an
52 # argument returns the total of the values in
53 # the list.
54 def get_total(value_list):
55 # Create a variable to use as an accumulator.
56 total = 0.0
57
58 # Calculate the total of the list elements.
59 for num in value_list:
60 total += num
61
62 # Return the total.
63 return total
64
65 # Call the main function.
66 if _ _name_ _ == '_ _main_ _':
67 main()

This function accepts a list as an argument. It uses an accumulator and a loop to calculate
the total of the values in the list. Line 64 returns the total.

Program Output (with input shown in bold)


Enter a test score: 92 Enter
Do you want to add another score?
Y = yes, anything else = no: y Enter

Enter a test score: 67 Enter


Do you want to add another score?
Y = yes, anything else = no: y Enter

Enter a test score: 75 Enter


Do you want to add another score?
Y = yes, anything else = no: y Enter

Enter a test score: 88 Enter


Do you want to add another score?
Y = yes, anything else = no: n Enter

Average with lowest score dropped: 85.0

Randomly Selecting List Elements


The random module provides a function named choice that randomly selects an element in
a list. You pass a list as an argument to the function, and the function returns the randomly
7.7 Processing Lists 393

selected element. To use the function, be sure to import the random module. The following
interactive session demonstrates:
>>> import random Enter
>>> names = ['Jenny', 'Kelly', 'Chloe', 'Aubrey'] Enter
>>> winner = random.choice(names) Enter
>>> print(winner) Enter
Chloe
>>>

The random module also provides a function named choices that returns multiple ran-
domly selected elements from a list. When you call the function, you pass a list and the
argument k=n, where n is the number of elements you want the function to return. The
function will then return a list of n randomly selected elements. The following interactive
session demonstrates:
>>> import random Enter
>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> selected = random.choices(numbers, k=3)
>>> print(selected)
[8, 7, 7]
>>>

The list that is returned from the choices function sometimes contains duplicate elements.
If you want to randomly select unique elements, use the random module’s sample function
instead. The following interactive session demonstrates:
>>> import random Enter
>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> selected = random.sample(numbers, k=3)
>>> print(selected)
[4, 10, 2]
>>>

Working with Lists and Files


Some tasks may require you to save the contents of a list to a file, so the data can be used
at a later time. Likewise, some situations may require you to read the data from a file into
a list. For example, suppose you have a file that contains a set of values that appear in
random order, and you want to sort the values. One technique for sorting the values in the
file would be to read them into a list, call the list’s sort method, then write the values in
the list back to the file.
Saving the contents of a list to a file is a straightforward procedure. In fact, Python file
objects have a method named writelines that writes an entire list to a file. A drawback
to the writelines method, however, is that it does not automatically write a newline
('\n') at the end of each item. Consequently, each item is written to one long line in the
file. Program 7-13 demonstrates the method.
394 Chapter 7   Lists and Tuples

Program 7-13 (writelines.py)

1 # This program uses the writelines method to save


2 # a list of strings to a file.
3
4 def main():
5 # Create a list of strings.
6 cities = ['New York', 'Boston', 'Atlanta', 'Dallas']
7
8 # Open a file for writing.
9 outfile = open('cities.txt', 'w')
10
11 # Write the list to the file.
12 outfile.writelines(cities)
13
14 # Close the file.
15 outfile.close()
16
17 # Call the main function.
18 if _ _name_ _ == '_ _main_ _':
19 main()

After this program executes, the cities.txt file will contain the following line:
New YorkBostonAtlantaDallas

An alternative approach is to use the for loop to iterate through the list, writing each ele-
ment with a terminating newline character. Program 7-14 shows an example.

Program 7-14 (write_list.py)

1 # This program saves a list of strings to a file.


2
3 def main():
4 # Create a list of strings.
5 cities = ['New York', 'Boston', 'Atlanta', 'Dallas']
6
7 # Open a file for writing.
8 outfile = open('cities.txt', 'w')
9
10 # Write the list to the file.
11 for item in cities:
12 outfile.write(item + '\n')
13
14 # Close the file.
15 outfile.close()
16
17 # Call the main function.
18 if _ _name_ _ == '_ _main_ _':
19 main()
7.7 Processing Lists 395

After this program executes, the cities.txt file will contain the following lines:
New York
Boston
Atlanta
Dallas

File objects in Python have a method named readlines that returns a file’s contents as
a list of strings. Each line in the file will be an item in the list. The items in the list will
include their terminating newline character, which in many cases you will want to strip.
Program 7-15 shows an example. The statement in line 8 reads the files contents into a
list, and the loop in lines 14 through 15 steps through the list, stripping the '\n' character
from each element.

Program 7-15 (read_list.py)

1 # This program reads a file's contents into a list.


2
3 def main():
4 # Open a file for reading.
5 infile = open('cities.txt', 'r')
6
7 # Read the contents of the file into a list.
8 cities = infile.readlines()
9
10 # Close the file.
11 infile.close()
12
13 # Strip the \n from each element.
14 for index in range(len(cities)):
15 cities[index] = cities[index].rstrip('\n')
16
17 # Print the contents of the list.
18 print(cities)
19
20 # Call the main function.
21 if _ _name_ _ == '_ _main_ _':
22    main()

Program Output
['New York', 'Boston', 'Atlanta', 'Dallas']

Program 7-16 shows another example of how a list can be written to a file. In this example,
a list of numbers is written. Notice in line 12, each item is converted to a string with the
str function, then a '\n' is concatenated to it. As an alternative, we could have written
line 12 using an f-string, as shown here:
outfile.write(f'{item}\n')
396 Chapter 7   Lists and Tuples

Program 7-16 (write_number_list.py)

1 # This program saves a list of numbers to a file.


2
3 def main():
4 # Create a list of numbers.
5 numbers = [1, 2, 3, 4, 5, 6, 7]
6
7 # Open a file for writing.
8 outfile = open('numberlist.txt', 'w')
9
10 # Write the list to the file.
11 for item in numbers:
12 outfile.write(str(item) + '\n')
13
14 # Close the file.
15 outfile.close()
16
17 # Call the main function.
18 if _ _name_ _ == '_ _main_ _':
19 main()

When you read numbers from a file into a list, the numbers will have to be converted from
strings to a numeric type. Program 7-17 shows an example.

Program 7-17 (read_number_list.py)

1 # This program reads numbers from a file into a list.


2
3 def main():
4 # Open a file for reading.
5 infile = open('numberlist.txt', 'r')
6
7 # Read the contents of the file into a list.
8 numbers = infile.readlines()
9
10 # Close the file.
11 infile.close()
12
13 # Convert each element to an int.
14 for index in range(len(numbers)):
15 numbers[index] = int(numbers[index])
16
17 # Print the contents of the list.
18 print(numbers)
19
7.8 List Comprehensions 397

20 # Call the main function.


21 if _ _name_ _ == '_ _main_ _':
22 main()

Program Output
[1, 2, 3, 4, 5, 6, 7]

7.8 List Comprehensions

CONCEPT: A list comprehension is a concise expression that creates a new list by


iterating over the elements of an existing list.

Some operations require you to read the contents of a list and use the values in that list to
produce a new list. For example, the following code makes a copy of a list:
list1 = [1, 2, 3, 4]
list2 = []

for item in list1:


list2.append(item)

In this code, each time the loop iterates, it appends an element of list1 to list2. After
this code executes, list2 will reference a copy of list1.
Code like this can be written in a more concise, compact manner using a list comprehen-
sion. A list comprehension is an expression that reads an input list, using the values of the
input list to produce an output list. For example, the previously shown code can be written
using a list comprehension as follows:
list1 = [1, 2, 3, 4]
list2 = [item for item in list1]

The list comprehension expression appears in the second line of code, enclosed in brackets.
As shown in Figure 7-6, the list comprehension begins with a result expression, followed
by an iteration expression. The iteration expression works like a for loop. Each time it
iterates, the target variable item is assigned the value of an element. At the end of each
iteration, the value of the result expression is appended to the new list.

Figure 7-6 Parts of a list comprehension for making a copy of a list


list2 = [item for item in list1]

Result Iteration
expression expression

The general format of a simple list comprehension is:


[result_expression iteration_expression]
398 Chapter 7   Lists and Tuples

Let’s look at another example. Suppose you have a list of numbers and you want to create
a second list that contains the squares of all the numbers in the first list. The following code
shows how to accomplish this using a for loop:
list1 = [1, 2, 3, 4]
list2 = []

for item in list1:


list2.append(item**2)

The following code shows how the same operation can be performed with a list comprehension:
list1 = [1, 2, 3, 4]
list2 = [item**2 for item in list1]

Figure 7-7 shows the iteration expression and the result expression. Each time the iteration
expression iterates, the target variable item is assigned the value of an element. At the end
of each iteration, the value of the result expression item**2 is appended to list2. After
this code executes, list2 will contain the values [1, 4, 9, 16].

Figure 7-7 Parts of a list comprehension for squaring elements in a list

list2 = [item**2 for item in list1]

Result Iteration
expression expression

Suppose you have a list of strings and you want to create a second list that contains the
lengths of all the strings in the first list. The following code shows how to accomplish this
using a for loop:
str_list = ['Winken', 'Blinken', 'Nod']
len_list = []

for s in str_list:
len_list.append(len(s))

The following code shows how the same operation can be performed with a list
­comprehension:
str_list = ['Winken', 'Blinken', 'Nod']
len_list = [len(s) for s in str_list]

In the list comprehension, the iteration expression is for s in str_list and the
result expression is len(s). After this code executes, len_list will contain the values
[6, 7, 3].

Using if Clauses with List Comprehensions


Sometimes you want to select only certain elements when processing a list. For example, sup-
pose a list contains integers and you want to make a second list that contains only the integers
from the first list that are less than 10. The following code accomplishes that with a loop:
list1 = [1, 12, 2, 20, 3, 15, 4]
list2 = []
7.9 Two-Dimensional Lists 399

for n in list1:
if n < 10:
list2.append(n)
The if statement that appears inside the for loop causes this code to append only the elements
that are less than 10 to list2. After this code executes, list2 will contain [1, 2, 3, 4].
This type of operation can also be accomplished by adding an if clause to a list comprehen-
sion. Here is the general format:
[result_expression iteration_expression if_clause]
The if clause acts as a filter, allowing you to select certain items from the input list. The
following code shows how we can rewrite the previous code using a list comprehension
with an if clause:
list1 = [1, 12, 2, 20, 3, 15, 4]
list2 = [item for item in list1 if item < 10]

In the list comprehension, the iteration expression is for item in list1, the if clause
is if item < 10, and the result expression is item. After this code executes, list2 will
contain [1, 2, 3, 4].
The following code shows another example:
last_names = ['Jackson', 'Smith', 'Hildebrandt', 'Jones']
short_names = [name for name in last_names if len(name) < 6]
The list comprehension, which iterates over the last_names list, has an if clause that
selects only the elements that are less than 6 characters long. After this code executes,
short_names will contain ['Smith', 'Jones']

Checkpoint
7.19 Look at the following list comprehension:
[x for x in my_list]
What is the result expression? What is the iteration expression?
7.20 After this code executes, what value will the list2 list hold?
list1 = [1, 12, 2, 20, 3, 15, 4]
list2 = [n*2 for n in list1]
7.21 After this code executes, what value will the list2 list hold?
list1 = [1, 12, 2, 20, 3, 15, 4]
list2 = [n for n in list1 if n > 10]

7.9 Two-Dimensional Lists

CONCEPT: A two-dimensional list is a list that has other lists as its elements.

The elements of a list can be virtually anything, including other lists. To demonstrate, look
at the following interactive session:
1 >>> students = [['Joe', 'Kim'], ['Sam', 'Sue'], ['Kelly', 'Chris']] Enter
2 >>> print(students) Enter
404 Chapter 7   Lists and Tuples

Checkpoint
7.22 Look at the following interactive session, in which a two-dimensional list is
created. How many rows and how many columns are in the list?
numbers = [[1, 2], [10, 20], [100, 200], [1000, 2000]]
7.23 Write a statement that creates a two-dimensional list with three rows and four
columns. Each element should be assigned the value 0.
7.24 Write a set of nested loops that display the contents of the numbers list shown in
Checkpoint question 7.22.

7.10 Tuples

CONCEPT: A tuple is an immutable sequence, which means that its contents cannot
be changed.

A tuple is a sequence, very much like a list. The primary difference between tuples and lists
is that tuples are immutable. That means once a tuple is created, it cannot be changed.
When you create a tuple, you enclose its elements in a set of parentheses, as shown in the
following interactive session:
>>> my_tuple = (1, 2, 3, 4, 5) Enter
>>> print(my_tuple) Enter
(1, 2, 3, 4, 5)
>>>

The first statement creates a tuple containing the elements 1, 2, 3, 4, and 5 and assigns it
to the variable my_tuple. The second statement sends my_tuple as an argument to the
print function, which displays its elements. The following session shows how a for loop
can iterate over the elements in a tuple:
>>> names = ('Holly', 'Warren', 'Ashley') Enter
>>> for n in names: Enter
print(n) Enter Enter
Holly
Warren
Ashley
>>>

Like lists, tuples support indexing, as shown in the following session:


>>> names = ('Holly', 'Warren', 'Ashley') Enter
>>> for i in range(len(names)): Enter
print(names[i]) Enter Enter

Holly
Warren
Ashley
>>>
7.10 Tuples 405

In fact, tuples support all the same operations as lists, except those that change the contents
of the list. Tuples support the following:
• Subscript indexing (for retrieving element values only)
• Methods such as index
• Built-in functions such as len, min, and max
• Slicing expressions
• The in operator
• The + and * operators
Tuples do not support methods such as append, remove, insert, reverse, and sort.

NOTE: If you want to create a tuple with just one element, you must write a trailing
comma after the element’s value, as shown here:
my_tuple = (1,) # Creates a tuple with one element.

If you omit the comma, you will not create a tuple. For example, the following state-
ment simply assigns the integer value 1 to the value variable:
value = (1) # Creates an integer.

What’s the Point?


If the only difference between lists and tuples is immutability, you might wonder why tuples
exist. One reason that tuples exist is performance. Processing a tuple is faster than process-
ing a list, so tuples are good choices when you are processing lots of data, and that data
will not be modified. Another reason is that tuples are safe. Because you are not allowed to
change the contents of a tuple, you can store data in one and rest assured that it will not
be modified (accidentally or otherwise) by any code in your program.
Additionally, there are certain operations in Python that require the use of a tuple. As you
learn more about Python, you will encounter tuples more frequently.

Converting Between Lists and Tuples


You can use the built-in list() function to convert a tuple to a list, and the built-in tuple()
function to convert a list to a tuple. The following interactive session demonstrates:
1 >>> number_tuple = (1, 2, 3) Enter
2 >>> number_list = list(number_tuple) Enter
3 >>> print(number_list) Enter
4 [1, 2, 3]
5 >>> str_list = ['one', 'two', 'three'] Enter
6 >>> str_tuple = tuple(str_list) Enter
7 >>> print(str_tuple) Enter
8 ('one', 'two', 'three')
9 >>>
406 Chapter 7   Lists and Tuples

Here’s a summary of the statements:


• Line 1 creates a tuple and assigns it to the number_tuple variable.
• Line 2 passes number_tuple to the list() function. The function returns a list contain-
ing the same values as number_tuple, and it is assigned to the number_list variable.
• Line 3 passes number_list to the print function. The function’s output is shown in line 4.
• Line 5 creates a list of strings and assigns it to the str_list variable.
• Line 6 passes str_list to the tuple() function. The function returns a tuple con-
taining the same values as str_list, and it is assigned to str_tuple.
• Line 7 passes str_tuple to the print function. The function’s output is shown in line 8.

Checkpoint
7.25 What is the primary difference between a list and a tuple?
7.26 Give two reasons why tuples exist.
7.27 Assume my_list references a list. Write a statement that converts it to a tuple.
7.28 Assume my_tuple references a tuple. Write a statement that converts it to a list.

7.11 Plotting List Data with the matplotlib Package


The matplotlib package is a library for creating two-dimensional charts and graphs. It is
not part of the standard Python library, so you will have to install it separately, after you
have installed Python on your system2. To install matplotlib on a Windows system, open
a command prompt window and enter the following command:
pip install matplotlib

On a Mac or a Linux system, open a Terminal window and enter the following command:
sudo pip3 install matplotlib

TIP: See Appendix G for more information about packages and the pip utility.

Once you enter the command, the pip utility will start downloading and installing the
package. Once the process is finished, you can verify that the package was correctly
installed by starting IDLE and entering the command
>>> import matplotlib

If you do not see an error message, you can assume the package was successfully installed.

Importing the pyplot Module


The matplotlib package contains a module named pyplot that you will need to import in
order to create all of the graphs that we will demonstrate in this chapter. There are several differ-
ent techniques for importing the module. Perhaps the most straightforward technique is like this:
import matplotlib.pyplot

2
At the time this was written, there was an installation bug with matplotlib and Python 3.8.
If you have trouble installing matplotlib with Python 3.8, try installing an earlier version of
Python, such as 3.7.5
CHAPTER

8 More About Strings

TOPICS
8.1 Basic String Operations 8.3 Testing, Searching, and Manipulating
8.2 String Slicing Strings

8.1 Basic String Operations

CONCEPT: Python provides several ways to access the individual characters in a


string. Strings also have methods that allow you to perform operations
on them.

Many of the programs that you have written so far have worked with strings, but only in
a limited way. The operations that you have performed with strings so far have primarily
involved only input and output. For example, you have read strings as input from the key-
board and from files, and sent strings as output to the screen and to files.
There are many types of programs that not only read strings as input and write strings as
output, but also perform operations on strings. Word processing programs, for example,
manipulate large amounts of text, and thus work extensively with strings. Email programs
and search engines are other examples of programs that perform operations on strings.
Python provides a wide variety of tools and programming techniques that you can use to
examine and manipulate strings. In fact, strings are a type of sequence, so many of the
concepts that you learned about sequences in Chapter 7 apply to strings as well. We will
look at many of these in this chapter.

431
432 Chapter 8   More About Strings

Accessing the Individual Characters in a String


Some programming tasks require that you access the individual characters in a string. For
example, you are probably familiar with websites that require you to set up a password.
For security reasons, many sites require that your password have at least one uppercase
letter, at least one lowercase letter, and at least one digit. When you set up your password,
a program examines each character to ensure that the password meets these qualifications.
(Later in this chapter, you will see an example of a program that does this sort of thing.)
In this section, we will look at two techniques that you can use in Python to access the
individual characters in a string: using the for loop, and indexing.

Iterating over a String with the for Loop


One of the easiest ways to access the individual characters in a string is to use the for loop.
Here is the general format:
for variable in string:
statement
statement
etc.

In the general format, variable is the name of a variable, and string is either a string
literal or a variable that references a string. Each time the loop iterates, variable will ref-
erence a copy of a character in string, beginning with the first character. We say that the
loop iterates over the characters in the string. Here is an example:
name = 'Juliet'
for ch in name:
print(ch)

The name variable references a string with six characters, so this loop will iterate six times.
The first time the loop iterates, the ch variable will reference 'J', the second time the loop
iterates the ch variable will reference 'u', and so forth. This is illustrated in Figure 8-1.
When the code executes, it will display the following:
J
u
l
i
e
t
8.1 Basic String Operations 433

Figure 8-1 Iterating over the string 'Juliet'

1st Iteration for ch in name: 2nd Iteration for ch in name:


print(ch) print(ch)

name 'Juliet' name 'Juliet'

ch 'J' ch 'u'

3rd Iteration for ch in name: 4th Iteration for ch in name:


print(ch) print(ch)

name 'Juliet' name 'Juliet'


ch 'l' ch 'i'

5th Iteration for ch in name: 6th Iteration for ch in name:


print(ch) print(ch)

name 'Juliet' name 'Juliet'


ch 'e' ch 't'

NOTE: Figure 8-1 illustrates how the ch variable references a copy of a character
from the string as the loop iterates. If we change the value that ch references in the loop,
it has no effect on the string referenced by name. To demonstrate, look at the following:
1 name = 'Juliet'
2 for ch in name:
3 ch = 'X'
4 print(name)

The statement in line 3 merely reassigns the ch variable to a different value each time
the loop iterates. It has no effect on the string 'Juliet' that is referenced by name, and
it has no effect on the number of times the loop iterates. When this code executes, the
statement in line 4 will print:
Juliet

Program 8-1 shows another example. This program asks the user to enter a string. It then
uses a for loop to iterate over the string, counting the number of times that the letter T
(uppercase or lowercase) appears.
434 Chapter 8   More About Strings

Program 8-1 (count_Ts.py)

1 # This program counts the number of times


2 # the letter T (uppercase or lowercase)
3 # appears in a string.
4
5 def main():
6 # Create a variable to use to hold the count.
7 # The variable must start with 0.
8 count = 0
9
10 # Get a string from the user.
11 my_string = input('Enter a sentence: ')
12
13 # Count the Ts.
14 for ch in my_string:
15 if ch == 'T' or ch == 't':
16 count += 1
17
18 # Print the result.
19 print(f’The letter T appears {count} times.’)
20
21 # Call the main function.
22 if _ _name_ _ == '_ _main_ _':
23 main()

Program Output (with input shown in bold)


Enter a sentence: Today we sold twenty-two toys. Enter
The letter T appears 5 times.

Indexing
Another way that you can access the individual characters in a string is with an index. Each
character in a string has an index that specifies its position in the string. Indexing starts
at 0, so the index of the first character is 0, the index of the second character is 1, and so
forth. The index of the last character in a string is 1 less than the number of characters in
the string. Figure 8-2 shows the indexes for each character in the string 'Roses are red'.
The string has 13 characters, so the character indexes range from 0 through 12.

Figure 8-2 String indexes

'R o s e s are r e d'


0 1 2 3 4 5 6 7 8 9 10 11 12

You can use an index to retrieve a copy of an individual character in a string, as shown here:
my_string = 'Roses are red'
ch = my_string[6]
8.1 Basic String Operations 435

The expression my_string[6] in the second statement returns a copy of the character at
index 6 in my_string. After this statement executes, ch will reference 'a' as shown in
Figure 8-3.

Figure 8-3 Getting a copy of a character from a string


my_string 'Roses are red'

ch 'a'

Here is another example:


my_string = 'Roses are red'
print(my_string[0], my_string[6], my_string[10])

This code will print the following:


R a r

You can also use negative numbers as indexes, to identify character positions relative to
the end of the string. The Python interpreter adds negative indexes to the length of the string
to determine the character position. The index −1 identifies the last character in a string, −2
identifies the next to last character, and so forth. The following code shows an example:
my_string = 'Roses are red'
print(my_string[−1], my_string[−2], my_string[−13])

This code will print the following:


d e R

IndexError Exceptions
An IndexError exception will occur if you try to use an index that is out of range for a
particular string. For example, the string 'Boston' has 6 characters, so the valid indexes
are 0 through 5. (The valid negative indexes are −1 through −6.) The following is an
example of code that causes an IndexError exception:
city = 'Boston'
print(city[6])

This type of error is most likely to happen when a loop incorrectly iterates beyond the end
of a string, as shown here:
city = 'Boston'
index = 0
while index < 7:
print(city[index])
index += 1

The last time that this loop iterates, the index variable will be assigned the value 6, which
is an invalid index for the string 'Boston'. As a result, the print function will cause an
IndexError exception to be raised.
436 Chapter 8   More About Strings

The len Function


In Chapter 7, you learned about the len function, which returns the length of a sequence. The
len function can also be used to get the length of a string. The following code demonstrates:

city = 'Boston'
size = len(city)

The second statement calls the len function, passing the city variable as an argument.
The function returns the value 6, which is the length of the string 'Boston'. This value is
assigned to the size variable.
The len function is especially useful to prevent loops from iterating beyond the end of a
string, as shown here:
city = 'Boston'
index = 0
while index < len(city):
print(city[index])
index += 1

Notice the loop iterates as long as index is less than the length of the string. This is because
the index of the last character in a string is always 1 less than the length of the string.

String Concatenation
A common operation that performed on strings is concatenation, or appending one string
to the end of another string. You have seen examples in earlier chapters that use the +
operator to concatenate strings. The + operator produces a string that is the combination
of the two strings used as its operands. The following interactive session demonstrates:
1 >>> message = 'Hello ' + 'world' Enter
2 >>> print(message) Enter
3 Hello world
4 >>>

Line 1 concatenates the strings 'Hello' and 'world' to produce the string 'Hello world'.
The string 'Hello world' is then assigned to the message variable. Line 2 prints the string
that is referenced by the message variable. The output is shown in line 3.
Here is another interactive session that demonstrates concatenation:
1 >>> first_name = 'Emily' Enter
2 >>> last_name = 'Yeager' Enter
3 >>> full_name = first_name + ' ' + last_name Enter
4 >>> print(full_name) Enter
5 Emily Yeager
6 >>>

Line 1 assigns the string 'Emily' to the first_name variable. Line 2 assigns the string
'Yeager' to the last_name variable. Line 3 produces a string that is the concatenation of
first_name, followed by a space, followed by last_name. The resulting string is assigned
to the full_name variable. Line 4 prints the string referenced by full_name. The output
is shown in line 5.
8.1 Basic String Operations 437

You can also use the += operator to perform concatenation. The following interactive ses-
sion demonstrates:
1 >>> letters = 'abc' Enter
2 >>> letters += 'def' Enter
3 >>> print(letters) Enter
4 abcdef
5 >>>

The statement in line 2 performs string concatenation. It works the same as


letters = letters + 'def'

After the statement in line 2 executes, the letters variable will reference the string 'abcdef'.
Here is another example:
>>> name = 'Kelly' Enter # name is 'Kelly'
>>> name += ' ' Enter # name is 'Kelly '
>>> name += 'Yvonne' Enter # name is 'Kelly Yvonne'
>>> name += ' ' Enter # name is 'Kelly Yvonne '
>>> name += 'Smith' Enter # name is 'Kelly Yvonne Smith'
>>> print(name) e
Kelly Yvonne Smith
>>>

Keep in mind that the operand on the left side of the += operator must be an existing vari-
able. If you specify a nonexistent variable, an exception is raised.

Strings Are Immutable


In Python, strings are immutable, which means once they are created, they cannot be
changed. Some operations, such as concatenation, give the impression that they modify
strings, but in reality they do not. For example, look at Program 8-2.

Program 8-2 (concatenate.py)

1 # This program concatenates strings.


2
3 def main():
4 name = 'Carmen'
5 print(f’The name is: {name}’)
6 name = name + ' Brown'
7 print(f’Now the name is: {name}’)
8
9 # Call the main function.
10 if _ _name_ _ == '_ _main_ _':
11 main()

Program Output
The name is: Carmen
Now the name is: Carmen Brown
438 Chapter 8   More About Strings

The statement in line 4 assigns the string 'Carmen' to the name variable, as shown
in Figure 8-4. The statement in line 6 concatenates the string ' Brown' to the string
'Carmen' and assigns the result to the name variable, as shown in Figure 8-5. As you can
see from the figure, the original string 'Carmen' is not modified. Instead, a new string con-
taining 'Carmen Brown' is created and assigned to the name variable. (The original string,
'Carmen' is no longer usable because no variable references it. The Python interpreter will
eventually remove the unusable string from memory.)

Figure 8-4 The string ‘Carmen’ assigned to name

name = 'Carmen'

name Carmen

Figure 8-5 The string ‘Carmen Brown’ assigned to name

name = name + ' Brown'

name Carmen

Carmen Brown

Because strings are immutable, you cannot use an expression in the form string[index]
on the left side of an assignment operator. For example, the following code will cause an
error:
# Assign 'Bill' to friend.
friend = 'Bill'
# Can we change the first character to 'J'?
friend[0] = 'J' # No, this will cause an error!

The last statement in this code will raise an exception because it attempts to change the
value of the first character in the string 'Bill'.

Checkpoint
8.1 Assume the variable name references a string. Write a for loop that prints each
character in the string.
8.2 What is the index of the first character in a string?
8.3 If a string has 10 characters, what is the index of the last character?
8.4 What happens if you try to use an invalid index to access a character in a string?
8.5 How do you find the length of a string?
8.6 What is wrong with the following code?
animal = 'Tiger'
animal[0] = 'L'
8.2 String Slicing 439

8.2 String Slicing

CONCEPT: You can use slicing expressions to select a range of characters


from a string

You learned in Chapter 7 that a slice is a span of items that are taken from a sequence.
When you take a slice from a string, you get a span of characters from within the string.
String slices are also called substrings.
To get a slice of a string, you write an expression in the following general format:
string[start : end]
In the general format, start is the index of the first character in the slice, and end is the
index marking the end of the slice. The expression will return a string containing a copy
of the characters from start up to (but not including) end. For example, suppose we have
the following:
full_name = 'Patty Lynn Smith'
middle_name = full_name[6:10]
The second statement assigns the string 'Lynn' to the middle_name variable. If you leave
out the start index in a slicing expression, Python uses 0 as the starting index. Here is an
example:
full_name = 'Patty Lynn Smith'
first_name = full_name[:5]
The second statement assigns the string 'Patty' to first_name. If you leave out the end
index in a slicing expression, Python uses the length of the string as the end index. Here is
an example:
full_name = 'Patty Lynn Smith'
last_name = full_name[11:]
The second statement assigns the string 'Smith' to last_name. What do you think the
following code will assign to the my_string variable?
full_name = 'Patty Lynn Smith'
my_string = full_name[:]
The second statement assigns the entire string 'Patty Lynn Smith' to my_string. The
statement is equivalent to:
my_string = full_name[0 : len(full_name)]
The slicing examples we have seen so far get slices of consecutive characters from strings.
Slicing expressions can also have step value, which can cause characters to be skipped in the
string. Here is an example of code that uses a slicing expression with a step value:
letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
print(letters[0:26:2])
The third number inside the brackets is the step value. A step value of 2, as used in this
example, causes the slice to contain every second character from the specified range in the
string. The code will print the following:
ACEGIKMOQSUWY
440 Chapter 8   More About Strings

You can also use negative numbers as indexes in slicing expressions to reference positions
relative to the end of the string. Here is an example:
full_name = 'Patty Lynn Smith'
last_name = full_name[−5:]

Recall that Python adds a negative index to the length of a string to get the position refer-
enced by that index. The second statement in this code assigns the string 'Smith' to the
last_name variable.

NOTE: Invalid indexes do not cause slicing expressions to raise an exception. For
example:
• If the end index specifies a position beyond the end of the string, Python will use the
length of the string instead.
• If the start index specifies a position before the beginning of the string, Python will
use 0 instead.
• If the start index is greater than the end index, the slicing expression will return
an empty string.

In the Spotlight:
Extracting Characters from a String
At a university, each student is assigned a system login name, which the student uses to log
into the campus computer system. As part of your internship with the university’s Informa-
tion Technology department, you have been asked to write the code that generates system
login names for students. You will use the following algorithm to generate a login name:
1. Get the first three characters of the student’s first name. (If the first name is less than
three characters in length, use the entire first name.)
2. Get the first three characters of the student’s last name. (If the last name is less than
three characters in length, use the entire last name.)
3. Get the last three characters of the student’s ID number. (If the ID number is less
than three characters in length, use the entire ID number.)
4. Concatenate the three sets of characters to generate the login name.
For example, if a student’s name is Amanda Spencer, and her ID number is ENG6721, her
login name would be AmaSpe721. You decide to write a function named get_login_name
that accepts a student’s first name, last name, and ID number as arguments, and returns the
student’s login name as a string. You will save the function in a module named login.py.
This module can then be imported into any Python program that needs to generate a login
name. Program 8-3 shows the code for the login.py module.

Program 8-3 (login.py)

1 # The get_login_name function accepts a first name,


2 # last name, and ID number as arguments. It returns
3 # a system login name.
4
8.2 String Slicing 441

5 def get_login_name(first, last, idnumber):


6 # Get the first three letters of the first name.
7 # If the name is less than 3 characters, the
8 # slice will return the entire first name.
9 set1 = first[0 : 3]
10
11 # Get the first three letters of the last name.
12 # If the name is less than 3 characters, the
13 # slice will return the entire last name.
14 set2 = last[0 : 3]
15
16 # Get the last three characters of the student ID.
17 # If the ID number is less than 3 characters, the
18 # slice will return the entire ID number.
19 set3 = idnumber[−3 :]
20
21 # Put the sets of characters together.
22 login_name = set1 + set2 + set3
23
24 # Return the login name.
25 return login_name

The get_login_name function accepts three string arguments: a first name, a last name,
and an ID number. The statement in line 9 uses a slicing expression to get the first three
characters of the string referenced by first and assigns those characters, as a string, to
the set1 variable. If the string referenced by first is less than three characters long, then
the value 3 will be an invalid ending index. If this is the case, Python will use the length
of the string as the ending index, and the slicing expression will return the entire string.
The statement in line 14 uses a slicing expression to get the first three characters of the
string referenced by last, and assigns those characters, as a string, to the set2 variable.
The entire string referenced by last will be returned if it is less than three characters.
The statement in line 19 uses a slicing expression to get the last three characters of the string
referenced by idnumber and assigns those characters, as a string, to the set3 variable. If
the string referenced by idnumber is less than three characters, then the value −3 will be an
invalid starting index. If this is the case, Python will use 0 as the starting index.
The statement in line 22 assigns the concatenation of set1, set2, and set3 to the login_
name variable. The variable is returned in line 25. Program 8-4 shows a demonstration of
the function.

Program 8-4 (generate_login.py)

1 # This program gets the user's first name, last name, and
2 # student ID number. Using this data it generates a
3 # system login name.
4
5 import login
(program continues)
442 Chapter 8   More About Strings

Program 8-4 (continued)

6
7 def main():
8 # Get the user's first name, last name, and ID number.
9 first = input('Enter your first name: ')
10 last = input('Enter your last name: ')
11 idnumber = input('Enter your student ID number: ')
12
13 # Get the login name.
14 print('Your system login name is:')
15 print(login.get_login_name(first, last, idnumber))
16
17 # Call the main function.
18 if _ _name_ _ == '_ _main_ _':
19 main()

Program Output (with input shown in bold)


Enter your first name: Holly Enter
Enter your last name: Gaddis Enter
Enter your student ID number: CSC34899 Enter
Your system login name is:
HolGad899

Program Output (with input shown in bold)


Enter your first name: Jo Enter
Enter your last name: Cusimano Enter
Enter your student ID number: BIO4497 Enter
Your system login name is:
JoCus497

Checkpoint
8.7 What will the following code display?
mystring = 'abcdefg'
print(mystring[2:5])
8.8 What will the following code display?
mystring = 'abcdefg'
print(mystring[3:])
8.9 What will the following code display?
mystring = 'abcdefg'
print(mystring[:3])
8.10 What will the following code display?
mystring = 'abcdefg'
print(mystring[:])
8.3 Testing, Searching, and Manipulating Strings 443

8.3 Testing, Searching, and Manipulating Strings

CONCEPT: Python provides operators and methods for testing strings, searching the
contents of strings, and getting modified copies of strings.

Testing Strings with in and not in


In Python, you can use the in operator to determine whether one string is contained in another
string. Here is the general format of an expression using the in operator with two strings:
string1 in string2

string1 and string2 can be either string literals or variables referencing strings. The expres-
sion returns true if string1 is found in string2. For example, look at the following code:
text = 'Four score and seven years ago'
if 'seven' in text:
print('The string "seven" was found.')
else:
print('The string "seven" was not found.')

This code determines whether the string 'Four score and seven years ago' contains
the string 'seven'. If we run this code, it will display:
The string "seven" was found.

You can use the not in operator to determine whether one string is not contained in
another string. Here is an example:
names = 'Bill Joanne Susan Chris Juan Katie'
if 'Pierre' not in names:
print('Pierre was not found.')
else:
print('Pierre was found.')

If we run this code, it will display:


Pierre was not found.

String Methods
Recall from Chapter 6 that a method is a function that belongs to an object and performs
some operation on that object. Strings in Python have numerous methods.1 In this section,
we will discuss several string methods for performing the following types of operations:
• Testing the values of strings
• Performing various modifications
• Searching for substrings and replacing sequences of characters

1
We do not cover all of the string methods in this book. For a comprehensive list of string methods,
see the Python documentation at www.python.org.
444 Chapter 8   More About Strings

Here is the general format of a string method call:


stringvar.method(arguments)

In the general format, stringvar is a variable that references a string, method is the name
of the method that is being called, and arguments is one or more arguments being passed
to the method. Let’s look at some examples.

String Testing Methods


The string methods shown in Table 8-1 test a string for specific characteristics. For exam-
ple, the isdigit method returns true if the string contains only numeric digits. Otherwise,
it returns false. Here is an example:
string1 = '1200'
if string1.isdigit():
print(f’{string1} contains only digits.’)
else:
print(f’{string1} contains characters other than digits.’)

This code will display


1200 contains only digits.

Here is another example:


string2 = '123abc'
if string2.isdigit():
print(f’{string2} contains only digits.’)
else:
print(f’{string2} contains characters other than digits.’)

This code will display


123abc contains characters other than digits.

Table 8-1 Some string testing methods


Method Description
isalnum() Returns true if the string contains only alphabetic letters or digits and is at
least one character in length. Returns false otherwise.
isalpha() Returns true if the string contains only alphabetic letters and is at least one
character in length. Returns false otherwise.
isdigit() Returns true if the string contains only numeric digits and is at least one
character in length. Returns false otherwise.
islower() Returns true if all of the alphabetic letters in the string are lowercase, and the
string contains at least one alphabetic letter. Returns false otherwise.
isspace() Returns true if the string contains only whitespace characters and is at least
one character in length. Returns false otherwise. (Whitespace characters are
spaces, newlines (\n), and tabs (\t).
isupper() Returns true if all of the alphabetic letters in the string are uppercase, and the
string contains at least one alphabetic letter. Returns false otherwise.
8.3 Testing, Searching, and Manipulating Strings 445

Program 8-5 demonstrates several of the string testing methods. It asks the user to enter a string
then displays various messages about the string, depending on the return value of the methods.

Program 8-5 (string_test.py)

1 # This program demonstrates several string testing methods.


2
3 def main():
4 # Get a string from the user.
5 user_string = input('Enter a string: ')
6
7 print('This is what I found about that string:')
8
9 # Test the string.
10 if user_string.isalnum():
11 print('The string is alphanumeric.')
12 if user_string.isdigit():
13 print('The string contains only digits.')
14 if user_string.isalpha():
15 print('The string contains only alphabetic characters.')
16 if user_string.isspace():
17 print('The string contains only whitespace characters.')
18 if user_string.islower():
19 print('The letters in the string are all lowercase.')
20 if user_string.isupper():
21 print('The letters in the string are all uppercase.')
22
23 # Call the string.
24 if _ _name_ _ == '_ _main_ _':
25 main()

Program Output (with input shown in bold)


Enter a string: abc Enter
This is what I found about that string:
The string is alphanumeric.
The string contains only alphabetic characters.
The letters in the string are all lowercase.

Program Output (with input shown in bold)


Enter a string: 123 Enter
This is what I found about that string:
The string is alphanumeric.
The string contains only digits.

Program Output (with input shown in bold)


Enter a string: 123ABC Enter
This is what I found about that string:
The string is alphanumeric.
The letters in the string are all uppercase.
446 Chapter 8   More About Strings

Modification Methods
Although strings are immutable, meaning they cannot be modified, they do have a number of
methods that return modified versions of themselves. Table 8-2 lists several of these methods.

Table 8-2 String Modification Methods


Method Description
lower() Returns a copy of the string with all alphabetic letters converted to lower-
case. Any character that is already lowercase, or is not an alphabetic letter, is
unchanged.
lstrip() Returns a copy of the string with all leading whitespace characters removed.
Leading whitespace characters are spaces, newlines (\n), and tabs (\t) that
appear at the beginning of the string.
lstrip(char) The char argument is a string containing a character. Returns a copy of the string
with all instances of char that appear at the beginning of the string removed.
rstrip() Returns a copy of the string with all trailing whitespace characters removed.
Trailing whitespace characters are spaces, newlines (\n), and tabs (\t) that
appear at the end of the string.
rstrip(char) The char argument is a string containing a character. The method returns
a copy of the string with all instances of char that appear at the end of the
string removed.
strip() Returns a copy of the string with all leading and trailing whitespace characters
removed.
strip(char) Returns a copy of the string with all instances of char that appear at the
beginning and the end of the string removed.
upper() Returns a copy of the string with all alphabetic letters converted to uppercase. Any
character that is already uppercase, or is not an alphabetic letter, is unchanged.

For example, the lower method returns a copy of a string with all of its alphabetic letters
converted to lowercase. Here is an example:
letters = 'WXYZ'
print(letters, letters.lower())

This code will print


WXYZ wxyz

The upper method returns a copy of a string with all of its alphabetic letters converted to
uppercase. Here is an example:
letters = 'abcd'
print(letters, letters.upper())

This code will print


abcd ABCD

The lower and upper methods are useful for making case-insensitive string compari­
sons. String comparisons are case-sensitive, which means the uppercase c­ haracters are
8.3 Testing, Searching, and Manipulating Strings 447

distinguished from the lowercase characters. For example, in a case-sensitive compari-


son, the string 'abc' is not considered the same as the string 'ABC' or the string 'Abc'
because the case of the characters are different. Sometimes it is more convenient to per-
form a case-insensitive comparison, in which the case of the characters is ignored. In a
case-insensitive comparison, the string 'abc' is considered the same as 'ABC' and 'Abc'.
For example, look at the following code:
again = 'y'
while again.lower() == 'y':
print('Hello')
print('Do you want to see that again?')
again = input('y = yes, anything else = no: ')

Notice the last statement in the loop asks the user to enter y to see the message displayed
again. The loop iterates as long as the expression again.lower() =='y' is true. The
expression will be true if the again variable references either 'y' or 'Y'.
Similar results can be achieved by using the upper method, as shown here:
again = 'y'
while again.upper() == 'Y':
print('Hello')
print('Do you want to see that again?')
again = input('y = yes, anything else = no: ')

Searching and Replacing


Programs commonly need to search for substrings, or strings that appear within other
strings. For example, suppose you have a document opened in your word processor, and
you need to search for a word that appears somewhere in it. The word that you are search-
ing for is a substring that appears inside a larger string, the document.
Table 8-3 lists some of the Python string methods that search for substrings, as well as a
method that replaces the occurrences of a substring with another string.

Table 8-3 Search and replace methods


Method Description
endswith(substring) The substring argument is a string. The method returns true if
the string ends with substring.
find(substring) The substring argument is a string. The method returns
the lowest index in the string where substring is found. If
substring is not found, the method returns −1.
replace(old, new) The old and new arguments are both strings. The method returns
a copy of the string with all instances of old replaced by new.
startswith(substring) The substring argument is a string. The method returns true if
the string starts with substring.
448 Chapter 8   More About Strings

The endswith method determines whether a string ends with a specified substring. Here
is an example:
filename = input('Enter the filename: ')
if filename.endswith('.txt'):
print('That is the name of a text file.')
elif filename.endswith('.py'):
print('That is the name of a Python source file.')
elif filename.endswith('.doc'):
print('That is the name of a word processing document.')
else:
print('Unknown file type.')

The startswith method works like the endswith method, but determines whether a
string begins with a specified substring.
The find method searches for a specified substring within a string. The method returns
the lowest index of the substring, if it is found. If the substring is not found, the method
returns −1. Here is an example:
string = 'Four score and seven years ago'
position = string.find('seven')
if position != −1:
print(f’The word “seven” was found at index {position}.’)
else:
print('The word "seven" was not found.')

This code will display


The word "seven" was found at index 15.

The replace method returns a copy of a string, where every occurrence of a specified
substring has been replaced with another string. For example, look at the following code:
string = 'Four score and seven years ago'
new_string = string.replace('years', 'days')
print(new_string)

This code will display


Four score and seven days ago

In the Spotlight:
Validating the Characters in a Password
At the university, passwords for the campus computer system must meet the following
requirements:
• The password must be at least seven characters long.
• It must contain at least one uppercase letter.
8.3 Testing, Searching, and Manipulating Strings 449

• It must contain at least one lowercase letter.


• It must contain at least one numeric digit.
When a student sets up his or her password, the password must be validated to ensure it
meets these requirements. You have been asked to write the code that performs this valida-
tion. You decide to write a function named valid_password that accepts the password
as an argument and returns either true or false, to indicate whether it is valid. Here is the
algorithm for the function, in pseudocode:
valid_password function:
Set the correct_length variable to false
Set the has_uppercase variable to false
Set the has_lowercase variable to false
Set the has_digit variable to false
If the password’s length is seven characters or greater:
Set the correct_length variable to true
for each character in the password:
if the character is an uppercase letter:
Set the has_uppercase variable to true
if the character is a lowercase letter:
Set the has_lowercase variable to true
if the character isa digit:
Set the has_digit variable to true
If correct_length and has_uppercase and has_lowercase and has_digit:
Set the is_valid variable to true
else:
Set the is_valid variable to false
Return the is_valid variable
Earlier (in the previous In the Spotlight section) you created a function named get_
login_name and stored that function in the login module. Because the valid_password
function’s purpose is related to the task of creating a student’s login account, you decide to
store the valid_password function in the login module as well. Program 8-6 shows the
login module with the valid_password function added to it. The function begins at line 34.

Program 8-6 (login.py)

1 # The get_login_name function accepts a first name,


2 # last name, and ID number as arguments. It returns
3 # a system login name.
4
5 def get_login_name(first, last, idnumber):
6 # Get the first three letters of the first name.
7 # If the name is less than 3 characters, the
8 # slice will return the entire first name.
9 set1 = first[0 : 3]
10
(program continues)
450 Chapter 8   More About Strings

Program 8-6 (continued)

11 # Get the first three letters of the last name.


12 # If the name is less than 3 characters, the
13 # slice will return the entire last name.
14 set2 = last[0 : 3]
15
16 # Get the last three characters of the student ID.
17 # If the ID number is less than 3 characters, the
18 # slice will return the entire ID number.
19 set3 = idnumber[−3 :]
20
21 # Put the sets of characters together.
22 login_name = set1 + set2 + set3
23
24 # Return the login name.
25 return login_name
26
27 # The valid_password function accepts a password as
28 # an argument and returns either true or false to
29 # indicate whether the password is valid. A valid
30 # password must be at least 7 characters in length,
31 # have at least one uppercase letter, one lowercase
32 # letter, and one digit.
33
34 def valid_password(password):
35 # Set the Boolean variables to false.
36 correct_length = False
37 has_uppercase = False
38 has_lowercase = False
39 has_digit = False
40
41 # Begin the validation. Start by testing the
42 # password's length.
43 if len(password) >= 7:
44 correct_length = True
45
46 # Test each character and set the
47 # appropriate flag when a required
48 # character is found.
49 for ch in password:
50 if ch.isupper():
51 has_uppercase = True
52 if ch.islower():
53 has_lowercase = True
54 if ch.isdigit():
55 has_digit = True
8.3 Testing, Searching, and Manipulating Strings 451

56
57 # Determine whether all of the requirements
58 # are met. If they are, set is_valid to true.
59 # Otherwise, set is_valid to false.
60 if correct_length and has_uppercase and \
61 has_lowercase and has_digit:
62 is_valid = True
63 else:
64 is_valid = False
65
66 # Return the is_valid variable.
67 return is_valid

Program 8-7 imports the login module and demonstrates the valid_password function.

Program 8-7 (validate_password.py)

1 # This program gets a password from the user and


2 # validates it.
3
4 import login
5
6 def main():
7 # Get a password from the user.
8 password = input('Enter your password: ')
9
10 # Validate the password.
11 while not login.valid_password(password):
12 print('That password is not valid.')
13 password = input('Enter your password: ')
14
15 print('That is a valid password.')
16
17 # Call the main function.
18 if _ _name_ _ == '_ _main_ _':
19 main()

Program Output (with input shown in bold)


Enter your password: bozo Enter
That password is not valid.
Enter your password: kangaroo Enter
That password is not valid.
Enter your password: Tiger9 Enter
That password is not valid.
Enter your password: Leopard6 Enter
That is a valid password.
452 Chapter 8   More About Strings

The Repetition Operator


In Chapter 7, you learned how to duplicate a list with the repetition operator (*). The repeti-
tion operator works with strings as well. Here is the general format:
string_to_copy * n

The repetition operator creates a string that contains n repeated copies of string_to_copy.
Here is an example:
my_string = 'w' * 5

After this statement executes, my_string will reference the string 'wwwww'. Here is another
example:
print('Hello' * 5)

This statement will print:


HelloHelloHelloHelloHello

Program 8-8 demonstrates the repetition operator.

Program 8-8 (repetition_operator.py)

1 # This program demonstrates the repetition operator.


2
3 def main():
4 # Print nine rows increasing in length.
5 for count in range(1, 10):
6 print('Z' * count)
7
8 # Print nine rows decreasing in length.
9 for count in range(8, 0, −1):
10 print('Z' * count)
11
12 # Call the main function.
13 if _ _name_ _ == '_ _main_ _':
14     main()

Program Output
Z
ZZ
ZZZ
ZZZZ
ZZZZZ
ZZZZZZ
ZZZZZZZ
ZZZZZZZZ
ZZZZZZZZZ
ZZZZZZZZ
8.3 Testing, Searching, and Manipulating Strings 453

ZZZZZZZ
ZZZZZZ
ZZZZZ
ZZZZ
ZZZ
ZZ
Z

Splitting a String
Strings in Python have a method named split that returns a list containing the words in
the string. Program 8-9 shows an example.

Program 8-9 (string_split.py)

1 # This program demonstrates the split method.


2
3 def main():
4 # Create a string with multiple words.
5 my_string = 'One two three four'
6
7 # Split the string.
8 word_list = my_string.split()
9
10 # Print the list of words.
11 print(word_list)
12
13 # Call the main function.
14 if _ _name_ _ == '_ _main_ _':
15 main()

Program Output
['One', 'two', 'three', 'four']

By default, the split method uses spaces as separators (that is, it returns a list of the words in
the string that are separated by spaces). You can specify a different separator by passing it as
an argument to the split method. For example, suppose a string contains a date, as shown here:
date_string = '11/26/2020'

If you want to break out the month, day, and year as items in a list, you can call the split
method using the '/' character as a separator, as shown here:
date_list = date_string.split('/')

After this statement executes, the date_list variable will reference this list:
['11', '26', '2020']
454 Chapter 8   More About Strings

Program 8-10 demonstrates this.

Program 8-10 (split_date.py)

1 # This program calls the split method, using the


2 # '/' character as a separator.
3
4 def main():
5 # Create a string with a date.
6 date_string = '11/26/2020'
7
8 # Split the date.
9 date_list = date_string.split('/')
10
11 # Display each piece of the date.
12 print(f'Month: {date_list[0]}')
13 print(f'Day: {date_list[1]}')
14 print(f'Year: {date_list[2]}')
15
16 # Call the main function.
17 if _ _name_ _ == '_ _main_ _':
18 main()

Program Output
Month: 11
Day: 26
Year: 2020

In the Spotlight:
String Tokens
Sometimes a string will contain a series of words or other items of data separated by spaces
or other characters. For example, look at the following string:
'peach raspberry strawberry vanilla'

This string contains the following four items of data: peach, raspberry, strawberry, and
vanilla. In programming terms, items such as these are known as tokens. Notice a space ap-
pears between the items. The character that separates tokens is known as a delimiter. Here
is another example:
'17;92;81;12;46;5'

This string contains the following tokens: 17, 92, 81, 12, 46, and 5. Notice a semicolon appears
between each item. In this example, the semicolon is used as a delimiter. Some programming
problems require you to read a string that contains a list of items then extract all the tokens
from the string for processing. For example, look at the following string that contains a date:
'3–22–2021'
CHAPTER

9 Dictionaries and Sets

TOPICS
9.1 Dictionaries
9.2 Sets
9.3 Serializing Objects

9.1 Dictionaries

CONCEPT: A dictionary is an object that stores a collection of data. Each element in


a dictionary has two parts: a key and a value. You use a key to locate a
specific value.

When you hear the word “dictionary,” you probably think about a large book such as the
Merriam-Webster dictionary, containing words and their definitions. If you want to know
VideoNote
Introduction to the meaning of a particular word, you locate it in the dictionary to find its definition.
Dictionaries
In Python, a dictionary is an object that stores a collection of data. Each element that is
stored in a dictionary has two parts: a key and a value. In fact, dictionary elements are
commonly referred to as key-value pairs. When you want to retrieve a specific value from a
dictionary, you use the key that is associated with that value. This is similar to the process
of looking up a word in the Merriam-Webster dictionary, where the words are keys and
the definitions are values.
For example, suppose each employee in a company has an ID number, and we want to write
a program that lets us look up an employee’s name by entering that employee’s ID number.
We could create a dictionary in which each element contains an employee ID number as
the key, and that employee’s name as the value. If we know an employee’s ID number, then
we can retrieve that employee’s name.
Another example would be a program that lets us enter a person’s name and gives us that
person’s phone number. The program could use a dictionary in which each element contains
a person’s name as the key, and that person’s phone number as the value. If we know a
person’s name, then we can retrieve that person’s phone number.

467
468 Chapter 9   Dictionaries and Sets

NOTE : Key-value pairs are often referred to as mappings because each key is mapped
to a value.

Creating a Dictionary
You can create a dictionary by enclosing the elements inside a set of curly braces ( {} ).
An element consists of a key, followed by a colon, followed by a value. The elements are
separated by commas. The following statement shows an example:
phonebook = {'Chris':'555−1111', 'Katie':'555−2222', 'Joanne':'555−3333'}

This statement creates a dictionary and assigns it to the phonebook variable. The dictionary
contains the following three elements:
• The first element is 'Chris':'555−1111'. In this element, the key is 'Chris' and
the value is '555−1111'.
• The second element is 'Katie':'555−2222'. In this element, the key is 'Katie' and
the value is '555−2222'.
• The third element is 'Joanne':'555−3333'. In this element, the key is 'Joanne' and
the value is '555−3333'.
In this example, the keys and the values are strings. The values in a dictionary can be
objects of any type, but the keys must be immutable objects. For example, keys can be
strings, integers, floating-point values, or tuples. Keys cannot be lists or any other type of
immutable object.

Retrieving a Value from a Dictionary


The elements in a dictionary are not stored in any particular order. For example, look at the
following interactive session in which a dictionary is created and its elements are displayed:
>>> phonebook = {'Chris':'555−1111', 'Katie':'555−2222',
'Joanne':'555−3333'} Enter
>>> phonebook Enter
{'Chris': '555−1111', 'Joanne': '555−3333', 'Katie': '555−2222'}
>>>

Notice the order in which the elements are displayed is different than the order in which
they were created. This illustrates how dictionaries are not sequences, like lists, tuples, and
strings. As a result, you cannot use a numeric index to retrieve a value by its position from
a dictionary. Instead, you use a key to retrieve a value.
To retrieve a value from a dictionary, you simply write an expression in the following
general format:
dictionary_name[key]

In the general format, dictionary_name is the variable that references the dictionary,
and key is a key. If the key exists in the dictionary, the expression returns the value that is­­
9.1 Dictionaries 469

associated with the key. If the key does not exist, a KeyError exception is raised. The
following interactive session demonstrates:
1 >>> phonebook = {'Chris':'555−1111', 'Katie':'555−2222',
'Joanne':'555−3333'} Enter
2 >>> phonebook['Chris'] Enter
3 '555−1111'
4 >>> phonebook['Joanne'] Enter
5 '555−3333'
6 >>> phonebook['Katie'] Enter
7 '555−2222'
8 >>> phonebook['Kathryn'] Enter
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
phonebook['Kathryn']
KeyError: 'Kathryn'
>>>

Let’s take a closer look at the session:


• Line 1 creates a dictionary containing names (as keys) and phone numbers (as values).
• In line 2, the expression phonebook['Chris'] returns the value from the phonebook
dictionary that is associated with the key 'Chris'. The value is displayed in line 3.
• In line 4, the expression phonebook['Joanne'] returns the value from the phonebook
dictionary that is associated with the key 'Joanne'. The value is displayed in line 5.
• In line 6, the expression phonebook['Katie'] returns the value from the phonebook
dictionary that is associated with the key 'Katie'. The value is displayed in line 7.
• In line 8, the expression phonebook['Kathryn'] is entered. There is no such key as
'Kathryn' in the phonebook dictionary, so a KeyError exception is raised.

NOTE: Remember that string comparisons are case sensitive. The expression
phonebook['katie'] will not locate the key 'Katie' in the dictionary.

Using the in and not in Operators to Test


for a Value in a Dictionary
As previously demonstrated, a KeyError exception is raised if you try to retrieve a value
from a dictionary using a nonexistent key. To prevent such an exception, you can use the
in operator to determine whether a key exists before you try to use it to retrieve a value.
The following interactive session demonstrates:
1 >>> phonebook = {'Chris':'555−1111', 'Katie':'555−2222',
'Joanne':'555−3333'} Enter
2 >>> if 'Chris' in phonebook: Enter
3 print(phonebook['Chris']) Enter Enter
4
5 555−1111
6 >>>
470 Chapter 9   Dictionaries and Sets

The if statement in line 2 determines whether the key 'Chris' is in the phonebook
dictionary. If it is, the statement in line 3 displays the value that is associated with that key.
You can also use the not in operator to determine whether a key does not exist, as dem-
onstrated in the following session:
1 >>> phonebook = {'Chris':'555−1111', 'Katie':'555−2222'} Enter
2 >>> if 'Joanne' not in phonebook: Enter
3 print('Joanne is not found.') Enter Enter
4
5 Joanne is not found.
6 >>>

NOTE: Keep in mind that string comparisons with the in and not in operators are
case sensitive.

Adding Elements to an Existing Dictionary


Dictionaries are mutable objects. You can add new key-value pairs to a dictionary with an
assignment statement in the following general format:
dictionary_name[key] = value

In the general format, dictionary_name is the variable that references the dictionary, and
key is a key. If key already exists in the dictionary, its associated value will be changed to
value. If the key does not exist, it will be added to the dictionary, along with value as its
associated value. The following interactive session demonstrates:
1 >>> phonebook = {'Chris':'555−1111', 'Katie':'555−2222',
'Joanne':'555−3333'} Enter
2 >>> phonebook['Joe'] = '555−0123' Enter
3 >>> phonebook['Chris'] = '555−4444' Enter
4 >>> phonebook Enter
5 {'Chris': '555−4444', 'Joanne': '555−3333', 'Joe': '555−0123',
'Katie': '555−2222'}
6 >>>

Let’s take a closer look at the session:


• Line 1 creates a dictionary containing names (as keys) and phone numbers (as values).
• The statement in line 2 adds a new key-value pair to the phonebook dictionary.
Because there is no key 'Joe' in the dictionary, this statement adds the key 'Joe',
along with its associated value '555−0123'.
• The statement in line 3 changes the value that is associated with an existing key.
Because the key 'Chris' already exists in the phonebook dictionary, this statement
changes its associated value to '555−4444'.
• Line 4 displays the contents of the phonebook dictionary. The output is shown in line 5.

NOTE: You cannot have duplicate keys in a dictionary. When you assign a value to
an existing key, the new value replaces the existing value.
9.1 Dictionaries 471

Deleting Elements
You can delete an existing key-value pair from a dictionary with the del statement. Here
is the general format:
del dictionary_name[key]
In the general format, dictionary_name is the variable that references the dictionary, and
key is a key. After the statement executes, the key and its associated value will be deleted
from the dictionary. If the key does not exist, a KeyError exception is raised. The following
interactive session demonstrates:
1 >>> phonebook = {'Chris':'555−1111', 'Katie':'555−2222',
'Joanne':'555−3333'} Enter
2 >>> phonebook Enter
3 {'Chris': '555−1111', 'Joanne': '555−3333', 'Katie': '555−2222'}
4 >>> del phonebook['Chris'] Enter
5 >>> phonebook Enter
6 {'Joanne': '555−3333', 'Katie': '555−2222'}
7 >>> del phonebook['Chris'] Enter
8 Traceback (most recent call last):
9 File "<pyshell#5>", line 1, in <module>
10 del phonebook['Chris']
11 KeyError: 'Chris'
12 >>>
Let’s take a closer look at the session:
• Line 1 creates a dictionary, and line 2 displays its contents.
• Line 4 deletes the element with the key 'Chris', and line 5 displays the contents of
the dictionary. You can see in the output in line 6 that the element no longer exists in
the dictionary.
• Line 7 tries to delete the element with the key 'Chris' again. Because the element no
longer exists, a KeyError exception is raised.
To prevent a KeyError exception from being raised, you should use the in operator to
determine whether a key exists before you try to delete it and its associated value. The fol-
lowing interactive session demonstrates:
1 >>> phonebook = {'Chris':'555−1111', 'Katie':'555−2222',
'Joanne':'555−3333'} Enter
2 >>> if 'Chris' in phonebook: Enter
3 del phonebook['Chris'] Enter Enter
4
5 >>> phonebook Enter
6 {'Joanne': '555−3333', 'Katie': '555−2222'}
7 >>>

Getting the Number of Elements in a Dictionary


You can use the built-in len function to get the number of elements in a dictionary. The
following interactive session demonstrates:
1 >>> phonebook = {'Chris':'555−1111', 'Katie':'555−2222'} Enter
2 >>> num_items = len(phonebook) Enter
472 Chapter 9   Dictionaries and Sets

3 >>> print(num_items) Enter


4 2
5 >>>

Here is a summary of the statements in the session:


• Line 1 creates a dictionary with two elements and assigns it to the phonebook variable.
• Line 2 calls the len function passing the phonebook variable as an argument. The
function returns the value 2, which is assigned to the num_items variable.
• Line 3 passes num_items to the print function. The function’s output is shown in
line 4.

Mixing Data Types in a Dictionary


As previously mentioned, the keys in a dictionary must be immutable objects, but their
associated values can be any type of object. For example, the values can be lists, as demon-
strated in the following interactive session. In this session, we create a dictionary in which
the keys are student names, and the values are lists of test scores.
1 >>> test_scores = { 'Kayla' : [88, 92, 100], Enter
2 'Luis' : [95, 74, 81], Enter
3 'Sophie' : [72, 88, 91], Enter
4 'Ethan' : [70, 75, 78] } Enter
5 >>> test_scores Enter
6 {'Kayla': [88, 92, 100], 'Sophie': [72, 88, 91], 'Ethan': [70, 75, 78],

7 'Luis': [95, 74, 81]}
8 >>> test_scores['Sophie'] Enter
9 [72, 88, 91]
10 >>> kayla_scores = test_scores['Kayla'] Enter
11 >>> print(kayla_scores) Enter
12 [88, 92, 100]
13 >>>

Let’s take a closer look at the session. This statement in lines 1 through 4 creates a dictio-
nary and assigns it to the test_scores variable. The dictionary contains the following
four elements:
• The first element is 'Kayla' : [88, 92, 100]. In this element, the key is 'Kayla'
and the value is the list [88, 92, 100].
• The second element is 'Luis' : [95, 74, 81]. In this element, the key is 'Luis'
and the value is the list [95, 74, 81].
• The third element is 'Sophie' : [72, 88, 91]. In this element, the key is 'Sophie'
and the value is the list [72, 88, 91].
• The fourth element is 'Ethan' : [70, 75, 78]. In this element, the key is 'Ethan'
and the value is the list [70, 75, 78].
Here is a summary of the rest of the session:
• Line 5 displays the contents of the dictionary, as shown in lines 6 through 7.
• Line 8 retrieves the value that is associated with the key 'Sophie'. The value is
displayed in line 9.
9.1 Dictionaries 473

• Line 10 retrieves the value that is associated with the key 'Kayla' and assigns it to
the kayla_scores variable. After this statement executes, the kayla_scores variable
references the list [88, 92, 100].
• Line 11 passes the kayla_scores variable to the print function. The function’s out-
put is shown in line 12.
The values that are stored in a single dictionary can be of different types. For example, one
element’s value might be a string, another element’s value might be a list, and yet another
­element’s value might be an integer. The keys can be of different types, too, as long as they
are immutable. The following interactive session demonstrates how different types can be
mixed in a dictionary:
1 >>> mixed_up = {'abc':1, 999:'yada yada', (3, 6, 9):[3, 6, 9]} Enter
2 >>> mixed_up Enter
3 {(3, 6, 9): [3, 6, 9], 'abc': 1, 999: 'yada yada'}
4 >>>

This statement in line 1 creates a dictionary and assigns it to the mixed_up variable. The
dictionary contains the following elements:
• The first element is 'abc':1. In this element, the key is the string 'abc' and the value
is the integer 1.
• The second element is 999:'yada yada'. In this element, the key is the integer 999
and the value is the string 'yada yada'.
• The third element is (3, 6, 9):[3, 6, 9]. In this element, the key is the tuple
(3, 6, 9) and the value is the list [3, 6, 9].

The following interactive session gives a more practical example. It creates a dictionary that
contains various pieces of data about an employee:
1 >>> employee = {'name' : 'Kevin Smith', 'id' : 12345, 'payrate' :
25.75 } Enter
2 >>> employee Enter
3 {'payrate': 25.75, 'name': 'Kevin Smith', 'id': 12345}
4 >>>

This statement in line 1 creates a dictionary and assigns it to the employee variable. The
dictionary contains the following elements:
• The first element is 'name' : 'Kevin Smith'. In this element, the key is the string
'name' and the value is the string 'Kevin Smith'.
• The second element is 'id' : 12345. In this element, the key is the string 'id' and
the value is the integer 12345.
• The third element is 'payrate' : 25.75. In this element, the key is the string
'payrate' and the value is the floating-point number 25.75.

Creating an Empty Dictionary


Sometimes, you need to create an empty dictionary and then add elements to it as the pro-
gram executes. You can use an empty set of curly braces to create an empty dictionary, as
demonstrated in the following interactive session:
1 >>> phonebook = {} Enter
2 >>> phonebook['Chris'] = '555−1111' Enter
474 Chapter 9   Dictionaries and Sets

3 >>> phonebook['Katie'] = '555−2222' Enter


4 >>> phonebook['Joanne'] = '555−3333' Enter
5 >>> phonebook Enter
6 {'Chris': '555−1111', 'Joanne': '555−3333', 'Katie': '555−2222'}
7 >>>

The statement in line 1 creates an empty dictionary and assigns it to the phonebook vari-
able. Lines 2 through 4 add key-value pairs to the dictionary, and the statement in line 5
displays the dictionary’s contents.
You can also use the built-in dict() method to create an empty dictionary, as shown in
the following statement:
phonebook = dict()

After this statement executes, the phonebook variable will reference an empty dictionary.

Using the for Loop to Iterate over a Dictionary


You can use the for loop in the following general format to iterate over all the keys in a
dictionary:
for var in dictionary:
statement
statement
etc.

In the general format, var is the name of a variable and dictionary is the name of a
dictionary. This loop iterates once for each element in the dictionary. Each time the loop
iterates, var is assigned a key. The following interactive session demonstrates:
1 >>> phonebook = {'Chris':'555−1111', Enter
2 'Katie':'555−2222', Enter
3 'Joanne':'555−3333'} Enter
4 >>> for key in phonebook: Enter
5 print(key) Enter Enter
6
7
8 Chris
9 Joanne
10 Katie
11 >>> for key in phonebook: Enter
12 print(key, phonebook[key]) Enter Enter
13
14
15 Chris 555−1111
16 Joanne 555−3333
17 Katie 555−2222
18 >>>

Here is a summary of the statements in the session:


• Lines 1 through 3 create a dictionary with three elements and assign it to the
phonebook variable.
9.1 Dictionaries 475

• Lines 4 through 5 contain a for loop that iterates once for each element of the
phonebook dictionary. Each time the loop iterates, the key variable is assigned a key.
Line 5 prints the value of the key variable. Lines 8 through 9 show the output of the
loop.
• Lines 11 through 12 contain another for loop that iterates once for each element of
the phonebook dictionary, assigning a key to the key variable. Line 5 prints the key
variable, followed by the value that is associated with that key. Lines 15 through 17
show the output of the loop.

Some Dictionary Methods


Dictionary objects have several methods. In this section, we look at some of the more useful
ones, which are summarized in Table 9-1.

Table 9-1 Some of the dictionary methods


Method Description
clear Clears the contents of a dictionary.
get Gets the value associated with a specified key. If the key is not found, the method
does not raise an exception. Instead, it returns a default value.
items Returns all the keys in a dictionary and their associated values as a sequence of
tuples.
keys Returns all the keys in a dictionary as a sequence of tuples.
pop Returns the value associated with a specified key and removes that key-value
pair from the dictionary. If the key is not found, the method returns a default
value.
popitem Returns, as a tuple, the key-value pair that was last added to the dictionary.
The method also removes the key-value pair from the dictionary.
values Returns all the values in the dictionary as a sequence of tuples.

The clear Method


The clear method deletes all the elements in a dictionary, leaving the dictionary empty.
The method’s general format is
dictionary.clear()

The following interactive session demonstrates the method:


1 >>> phonebook = {'Chris':'555−1111', 'Katie':'555−2222'} Enter
2 >>> phonebook Enter
3 {'Chris': '555−1111', 'Katie': '555−2222'}
4 >>> phonebook.clear() Enter
5 >>> phonebook Enter
6 {}
7 >>>
476 Chapter 9   Dictionaries and Sets

Notice after the statement in line 4 executes, the phonebook dictionary contains no
elements.

The get Method


You can use the get method as an alternative to the [] operator for getting a value from
a dictionary. The get method does not raise an exception if the specified key is not found.
Here is the method’s general format:
dictionary.get(key, default)

In the general format, dictionary is the name of a dictionary, key is a key to search for
in the dictionary, and default is a default value to return if the key is not found. When
the method is called, it returns the value that is associated with the specified key. If the
specified key is not found in the dictionary, the method returns default. The following
interactive session demonstrates:
1 >>> phonebook = {'Chris':'555−1111', 'Katie':'555−2222'} Enter
2 >>> value = phonebook.get('Katie', 'Entry not found') Enter
3 >>> print(value) Enter
4 555−2222
5 >>> value = phonebook.get('Andy', 'Entry not found') Enter
6 >>> print(value) Enter
7 Entry not found
8 >>>

Let’s take a closer look at the session:


• The statement in line 2 searches for the key 'Katie' in the phonebook dictionary. The
key is found, so its associated value is returned and assigned to the value variable.
• Line 3 passes the value variable to the print function. The function’s output is
shown in line 4.
• The statement in line 5 searches for the key 'Andy' in the phonebook dictionary. The
key is not found, so the string 'Entry not found' is assigned to the value variable.
• Line 6 passes the value variable to the print function. The function’s output is
shown in line 7.

The items Method


The items method returns all of a dictionary’s keys and their associated values. They are
returned as a special type of sequence known as a dictionary view. Each element in the dic-
tionary view is a tuple, and each tuple contains a key and its associated value. For example,
suppose we have created the following dictionary:
phonebook = {'Chris':'555−1111', 'Katie':'555−2222', 'Joanne':'555−3333'}

If we call the phonebook.items() method, it returns the following sequence:


[('Chris', '555−1111'), ('Joanne', '555−3333'), ('Katie', '555−2222')]

Notice the following:


• The first element in the sequence is the tuple ('Chris', '555−1111').
• The second element in the sequence is the tuple ('Joanne', '555−3333').
• The third element in the sequence is the tuple ('Katie', '555−2222').
9.1 Dictionaries 477

You can use the for loop to iterate over the tuples in the sequence. The following interac-
tive session demonstrates:
1 >>> phonebook = {'Chris':'555−1111', Enter
2 'Katie':'555−2222', Enter
3 'Joanne':'555−3333'} Enter
4 >>> for key, value in phonebook.items(): Enter
5 print(key, value) Enter Enter
6
7
8 Chris 555−1111
9 Joanne 555−3333
10 Katie 555−2222
11 >>>

Here is a summary of the statements in the session:


• Lines 1 through 3 create a dictionary with three elements and assign it to the phonebook
variable.
• The for loop in lines 4 through 5 calls the phonebook.items() method, which
returns a sequence of tuples containing the key-value pairs in the dictionary. The loop
iterates once for each tuple in the sequence. Each time the loop iterates, the values
of a tuple are assigned to the key and value variables. Line 5 prints the value of the
key variable, followed by the value of the value variable. Lines 8 through 10 show
the output of the loop.

The keys Method


The keys method returns all of a dictionary’s keys as a dictionary view, which is a type of
sequence. Each element in the dictionary view is a key from the dictionary. For example,
suppose we have created the following dictionary:
phonebook = {'Chris':'555−1111', 'Katie':'555−2222', 'Joanne':'555−3333'}

If we call the phonebook.keys() method, it will return the following sequence:


['Chris', 'Joanne', 'Katie']

The following interactive session shows how you can use a for loop to iterate over the
sequence that is returned from the keys method:
1 >>> phonebook = {'Chris':'555−1111', Enter
2 'Katie':'555−2222', Enter
3 'Joanne':'555−3333'} Enter
4 >>> for key in phonebook.keys(): Enter
5 print(key) Enter Enter
6
7
8 Chris
9 Joanne
10 Katie
11 >>>
478 Chapter 9   Dictionaries and Sets

The pop Method


The pop method returns the value associated with a specified key and removes that key-
value pair from the dictionary. If the key is not found, the method returns a default value.
Here is the method’s general format:
dictionary.pop(key, default)

In the general format, dictionary is the name of a dictionary, key is a key to search for
in the dictionary, and default is a default value to return if the key is not found. When
the method is called, it returns the value that is associated with the specified key, and it
removes that key-value pair from the dictionary. If the specified key is not found in the
dictionary, the method returns default. The following interactive session demonstrates:
1 >>> phonebook = {'Chris':'555−1111', Enter
2 'Katie':'555−2222', Enter
3 'Joanne':'555−3333'} Enter
4 >>> phone_num = phonebook.pop('Chris', 'Entry not found') Enter
5 >>> phone_num Enter
6 '555−1111'
7 >>> phonebook Enter
8 {'Joanne': '555−3333', 'Katie': '555−2222'}
9 >>> phone_num = phonebook.pop('Andy', 'Element not found') Enter
10 >>> phone_num Enter
11 'Element not found'
12 >>> phonebook Enter
13 {'Joanne': '555−3333', 'Katie': '555−2222'}
14 >>>

Here is a summary of the statements in the session:


• Lines 1 through 3 create a dictionary with three elements and assign it to the phonebook
variable.
• Line 4 calls the phonebook.pop() method, passing 'Chris' as the key to search for.
The value that is associated with the key 'Chris' is returned and assigned to the
phone_num variable. The key-value pair containing the key 'Chris' is removed from
the dictionary.
• Line 5 displays the value assigned to the phone_num variable. The output is displayed
in line 6. Notice this is the value that was associated with the key 'Chris'.
• Line 7 displays the contents of the phonebook dictionary. The output is shown in
line 8. Notice the key-value pair that contained the key 'Chris' is no longer in the
dictionary.
• Line 9 calls the phonebook.pop() method, passing 'Andy' as the key to search for.
The key is not found, so the string 'Entry not found' is assigned to the phone_num
variable.
• Line 10 displays the value assigned to the phone_num variable. The output is dis-
played in line 11.
• Line 12 displays the contents of the phonebook dictionary. The output is shown in
line 13.
9.1 Dictionaries 479

The popitem Method


The popitem method performs two actions: (1) It removes the key-value pair that was
last added to the dictionary, and (2) it returns that key-value pair as a tuple. Here is the
method’s general format:
dictionary.popitem()

You can use an assignment statement in the following general format to assign the returned
key and value to individual variables:
k, v = dictionary.popitem()

This type of assignment is known as a multiple assignment because multiple variables are
being assigned at once. In the general format, k and v are variables. After the statement
executes, k is assigned a randomly selected key from the dictionary, and v is assigned
the value associated with that key. The key-value pair is removed from the dictionary.
The following interactive session demonstrates:
1 >>> phonebook = {'Chris':'555−1111', Enter
2 'Katie':'555−2222', Enter
3 'Joanne':'555−3333'} Enter
4 >>> phonebook Enter
5 {'Chris': '555−1111', 'Joanne': '555−3333', 'Katie': '555−2222'}
6 >>> key, value = phonebook.popitem() Enter
7 >>> print(key, value) Enter
8 Chris 555−1111
9 >>> phonebook Enter
10 {'Joanne': '555−3333', 'Katie': '555−2222'}
11 >>>

Here is a summary of the statements in the session:


• Lines 1 through 3 create a dictionary with three elements and assign it to the phonebook
variable.
• Line 4 displays the dictionary’s contents, shown in line 5.
• Line 6 calls the phonebook.popitem() method. The key and value that are returned
from the method are assigned to the variables key and value. The key-value pair is
removed from the dictionary.
• Line 7 displays the values assigned to the key and value variables. The output is
shown in line 8.
• Line 9 displays the contents of the dictionary. The output is shown in line 10. Notice
that the key-value pair that was returned from the popitem method in line 6 has been
removed.
Keep in mind that the popitem method raises a KeyError exception if it is called on an
empty dictionary.

The values Method


The values method returns all a dictionary’s values (without their keys) as a dictionary
view, which is a type of sequence. Each element in the dictionary view is a value from the
dictionary. For example, suppose we have created the following dictionary:
phonebook = {'Chris':'555−1111', 'Katie':'555−2222', 'Joanne':'555−3333'}
480 Chapter 9   Dictionaries and Sets

If we call the phonebook.values() method, it returns the following sequence:


['555−1111', '555−2222', '555−3333']

The following interactive session shows how you can use a for loop to iterate over the
sequence that is returned from the values method:
1 >>> phonebook = {'Chris':'555−1111', Enter
2 'Katie':'555−2222', Enter
3 'Joanne':'555−3333'} Enter
4 >>> for val in phonebook.values(): Enter
5 print(val) Enter Enter
6
7
8 555−1111
9 555−3333
10 555−2222
11 >>>

In the Spotlight:
Using a Dictionary to Simulate a Deck of Cards
In some games involving poker cards, the cards are assigned numeric values. For example,
in the game of Blackjack, the cards are given the following numeric values:
• Numeric cards are assigned the value they have printed on them. For example, the
value of the 2 of spades is 2, and the value of the 5 of diamonds is 5.
• Jacks, queens, and kings are valued at 10.
• Aces are valued at either 1 or 11, depending on the player’s choice.
In this section, we look at a program that uses a dictionary to simulate a standard deck of
poker cards, where the cards are assigned numeric values similar to those used in Blackjack.
(In the program, we assign the value 1 to all aces.) The key-value pairs use the name of the
card as the key, and the card’s numeric value as the value. For example, the key-value pair
for the queen of hearts is
'Queen of Hearts':10

And the key-value pair for the 8 of diamonds is


'8 of Diamonds':8
The program prompts the user for the number of cards to deal, and it randomly deals a
hand of that many cards from the deck. The names of the cards are displayed, as well as
the total numeric value of the hand. Program 9-1 shows the program code. The program is
divided into three functions: main, create_deck, and deal_cards. Rather than presenting
the entire program at once, let’s first examine the main function.
9.2 Sets 493

9.5 What will the following code display?


stuff = {1 : 'aaa', 2 : 'bbb', 3 : 'ccc'}
print(stuff[3])
9.6 How can you determine whether a key-value pair exists in a dictionary?
9.7 Suppose a dictionary named inventory exists. What does the following statement do?
del inventory[654]
9.8 What will the following code display?
stuff = {1 : 'aaa', 2 : 'bbb', 3 : 'ccc'}
print(len(stuff))

9.9 What will the following code display?


stuff = {1 : 'aaa', 2 : 'bbb', 3 : 'ccc'}
for k in stuff:
print(k)
9.10 What is the difference between the dictionary methods pop and popitem?
9.11 What does the items method return?
9.12 What does the keys method return?
9.13 What does the values method return?
9.14 Assume the following list exists:
names = ['Chris', 'Katie', 'Joanne', 'Kurt']
Write a statement that uses a dictionary comprehension to create a dictionary in
which each element contains a name from the names list as its key, and the length
of that name as its value.
9.15 Assume the following dictionary exists:
phonebook = {'Chris':'919-555−1111', 'Katie':'828-555−2222',
'Joanne':'704-555−3333', 'Kurt':'919-555−3333'}
Write a statement that uses a dictionary comprehension to create a second dictionary
containing the elements of phonebook that have a value starting with '919'.

9.2 Sets

CONCEPT: A set contains a collection of unique values and works like a


mathematical set.

A set is an object that stores a collection of data in the same way as mathematical sets. Here
are some important things to know about sets:
VideoNote
Introduction
to Sets • All the elements in a set must be unique. No two elements can have the same value.
• Sets are unordered, which means that the elements in a set are not stored in any par-
ticular order.
• The elements that are stored in a set can be of different data types.
494 Chapter 9   Dictionaries and Sets

Creating a Set
To create a set, you have to call the built-in set function. Here is an example of how you
create an empty set:
myset = set()

After this statement executes, the myset variable will reference an empty set. You can also
pass one argument to the set function. The argument that you pass must be an object that
contains iterable elements, such as a list, a tuple, or a string. The individual elements of
the object that you pass as an argument become elements of the set. Here is an example:
myset = set(['a', 'b', 'c'])

In this example, we are passing a list as an argument to the set function. After this statement
executes, the myset variable references a set containing the elements 'a', 'b', and 'c'.
If you pass a string as an argument to the set function, each individual character in the
string becomes a member of the set. Here is an example:
myset = set('abc')

After this statement executes, the myset variable will reference a set containing the elements
'a', 'b', and 'c'.

Sets cannot contain duplicate elements. If you pass an argument containing duplicate
elements to the set function, only one of the duplicated elements will appear in the set.
Here is an example:
myset = set('aaabc')

The character 'a' appears multiple times in the string, but it will appear only once in the
set. After this statement executes, the myset variable will reference a set containing the
elements 'a', 'b', and 'c'.
What if you want to create a set in which each element is a string containing more than one
character? For example, how would you create a set containing the elements 'one', 'two',
and 'three'? The following code does not accomplish the task, because you can pass no
more than one argument to the set function:
# This is an ERROR!
myset = set('one', 'two', 'three')

The following does not accomplish the task either:


# This does not do what we intend.
myset = set('one two three')

After this statement executes, the myset variable will reference a set containing the ele-
ments 'o', 'n', 'e', ' ', 't', 'w', 'h', and 'r'. To create the set that we want, we have
to pass a list containing the strings ‘one’, ‘two’, and ‘three’ as an argument to the set
function. Here is an example:
# OK, this works.
myset = set(['one', 'two', 'three'])

After this statement executes, the myset variable will reference a set containing the ele-
ments 'one', 'two', and 'three'.
9.2 Sets 495

Getting the Number of Elements in a Set


As with lists, tuples, and dictionaries, you can use the len function to get the number of
elements in a set. The following interactive session demonstrates:
1 >>> myset = set([1, 2, 3, 4, 5]) Enter
2 >>> len(myset) Enter
3 5
4 >>>

Adding and Removing Elements


Sets are mutable objects, so you can add items to them and remove items from them.
You use the add method to add an element to a set. The following interactive session
demonstrates:
1 >>> myset = set() Enter
2 >>> myset.add(1) Enter
3 >>> myset.add(2) Enter
4 >>> myset.add(3) Enter
5 >>> myset Enter
6 {1, 2, 3}
7 >>> myset.add(2) Enter
8 >>> myset
9 {1, 2, 3}

The statement in line 1 creates an empty set and assigns it to the myset variable. The state-
ments in lines 2 through 4 add the values 1, 2, and 3 to the set. Line 5 displays the contents
of the set, which is shown in line 6.
The statement in line 7 attempts to add the value 2 to the set. The value 2 is already in the
set, however. If you try to add a duplicate item to a set with the add method, the method
does not raise an exception. It simply does not add the item.
You can add a group of elements to a set all at one time with the update method. When
you call the update method as an argument, you pass an object that contains iterable ele-
ments, such as a list, a tuple, string, or another set. The individual elements of the object
that you pass as an argument become elements of the set. The following interactive session
demonstrates:
1 >>> myset = set([1, 2, 3]) Enter
2 >>> myset.update([4, 5, 6]) Enter
3 >>> myset Enter
4 {1, 2, 3, 4, 5, 6}
5 >>>

The statement in line 1 creates a set containing the values 1, 2, and 3. Line 2 adds the values
4, 5, and 6. The following session shows another example:
1 >>> set1 = set([1, 2, 3]) Enter
2 >>> set2 = set([8, 9, 10]) Enter
3 >>> set1.update(set2) Enter
496 Chapter 9   Dictionaries and Sets

4 >>> set1
5 {1, 2, 3, 8, 9, 10}
6 >>> set2 Enter
7 {8, 9, 10}
8 >>>

Line 1 creates a set containing the values 1, 2, and 3 and assigns it to the set1 variable.
Line 2 creates a set containing the values 8, 9, and 10 and assigns it to the set2 variable.
Line 3 calls the set1.update method, passing set2 as an argument. This causes the ele-
ment of set2 to be added to set1. Notice set2 remains unchanged. The following session
shows another example:
1 >>> myset = set([1, 2, 3]) Enter
2 >>> myset.update('abc') Enter
3 >>> myset Enter
4 {'a', 1, 2, 3, 'c', 'b'}
5 >>>

The statement in line 1 creates a set containing the values 1, 2, and 3. Line 2 calls the
myset.update method, passing the string 'abc' as an argument. This causes the each
character of the string to be added as an element to myset.
You can remove an item from a set with either the remove method or the discard method.
You pass the item that you want to remove as an argument to either method, and that item
is removed from the set. The only difference between the two methods is how they behave
when the specified item is not found in the set. The remove method raises a KeyError
exception, but the discard method does not raise an exception. The following interactive
session demonstrates:
1 >>> myset = set([1, 2, 3, 4, 5]) Enter
2 >>> myset Enter
3 {1, 2, 3, 4, 5}
4 >>> myset.remove(1) Enter
5 >>> myset Enter
6 {2, 3, 4, 5}
7 >>> myset.discard(5) Enter
8 >>> myset Enter
9 {2, 3, 4}
10 >>> myset.discard(99) Enter
11 >>> myset.remove(99) Enter
12 Traceback (most recent call last):
13 File "<pyshell#12>", line 1, in <module>
14 myset.remove(99)
15 KeyError: 99
16 >>>

Line 1 creates a set with the elements 1, 2, 3, 4, and 5. Line 2 displays the contents of the
set, which is shown in line 3. Line 4 calls the remove method to remove the value 1 from the
set. You can see in the output shown in line 6 that the value 1 is no longer in the set. Line 7
calls the discard method to remove the value 5 from the set. You can see in the output in
line 9 that the value 5 is no longer in the set. Line 10 calls the discard method to remove
9.2 Sets 497

the value 99 from the set. The value is not found in the set, but the discard method does
not raise an exception. Line 11 calls the remove method to remove the value 99 from the
set. Because the value is not in the set, a KeyError exception is raised, as shown in lines 12
through 15.
You can clear all the elements of a set by calling the clear method. The following interac-
tive session demonstrates:
1 >>> myset = set([1, 2, 3, 4, 5]) Enter
2 >>> myset Enter
3 {1, 2, 3, 4, 5}
4 >>> myset.clear() Enter
5 >>> myset Enter
6 set()
7 >>>

The statement in line 4 calls the clear method to clear the set. Notice in line 6 that when
we display the contents of an empty set, the interpreter displays set().

Using the for Loop to Iterate over a Set


You can use the for loop in the following general format to iterate over all the elements
in a set:
for var in set:
statement
statement
etc.

In the general format, var is the name of a variable and set is the name of a set. This loop
iterates once for each element in the set. Each time the loop iterates, var is assigned an ele-
ment. The following interactive session demonstrates:
1 >>> myset = set(['a', 'b', 'c']) Enter
2 >>> for val in myset: Enter
3 print(val) Enter Enter
4
5 a
6 c
7 b
8 >>>

Lines 2 through 3 contain a for loop that iterates once for each element of the myset set.
Each time the loop iterates, an element of the set is assigned to the val variable. Line 3
prints the value of the val variable. Lines 5 through 7 show the output of the loop.

Using the in and not in Operators to Test


for a Value in a Set
You can use the in operator to determine whether a value exists in a set. The following
interactive session demonstrates:
498 Chapter 9   Dictionaries and Sets

1 >>> myset = set([1, 2, 3]) Enter


2 >>> if 1 in myset: Enter
3 print('The value 1 is in the set.') Enter Enter
4
5 The value 1 is in the set.
6 >>>

The if statement in line 2 determines whether the value 1 is in the myset set. If it is, the
statement in line 3 displays a message.
You can also use the not in operator to determine if a value does not exist in a set, as
demonstrated in the following session:
1 >>> myset = set([1, 2, 3]) Enter
2 >>> if 99 not in myset: Enter
3 print('The value 99 is not in the set.') Enter Enter
4
5 The value 99 is not in the set.
6 >>>

Finding the Union of Sets


The union of two sets is a set that contains all the elements of both sets. In Python, you can
call the union method to get the union of two sets. Here is the general format:
set1.union(set2)

In the general format, set1 and set2 are sets. The method returns a set that contains the
elements of both set1 and set2. The following interactive session demonstrates:
1 >>> set1 = set([1, 2, 3, 4]) Enter
2 >>> set2 = set([3, 4, 5, 6]) Enter
3 >>> set3 = set1.union(set2) Enter
4 >>> set3 Enter
5 {1, 2, 3, 4, 5, 6}
6 >>>

The statement in line 3 calls the set1 object’s union method, passing set2 as an argument.
The method returns a set that contains all the elements of set1 and set2 (without dupli-
cates, of course). The resulting set is assigned to the set3 variable.
You can also use the | operator to find the union of two sets. Here is the general format of
an expression using the | operator with two sets:
set1 | set2

In the general format, set1 and set2 are sets. The expression returns a set that contains the
elements of both set1 and set2. The following interactive session demonstrates:
1 >>> set1 = set([1, 2, 3, 4]) Enter
2 >>> set2 = set([3, 4, 5, 6]) Enter
3 >>> set3 = set1 | set2 Enter
4 >>> set3 Enter
5 {1, 2, 3, 4, 5, 6}
6 >>>
9.2 Sets 499

Finding the Intersection of Sets


The intersection of two sets is a set that contains only the elements that are found in both
sets. In Python, you can call the intersection method to get the intersection of two sets.
Here is the general format:
set1.intersection(set2)

In the general format, set1 and set2 are sets. The method returns a set that contains the ele-
ments that are found in both set1 and set2. The following interactive session demonstrates:
1 >>> set1 = set([1, 2, 3, 4]) Enter
2 >>> set2 = set([3, 4, 5, 6]) Enter
3 >>> set3 = set1.intersection(set2) Enter
4 >>> set3 Enter
5 {3, 4}
6 >>>

The statement in line 3 calls the set1 object’s intersection method, passing set2 as an
argument. The method returns a set that contains the elements that are found in both set1
and set2. The resulting set is assigned to the set3 variable.
You can also use the & operator to find the intersection of two sets. Here is the general
format of an expression using the & operator with two sets:
set1 & set2

In the general format, set1 and set2 are sets. The expression returns a set that contains the
elements that are found in both set1 and set2. The following interactive session demonstrates:
1 >>> set1 = set([1, 2, 3, 4]) Enter
2 >>> set2 = set([3, 4, 5, 6]) Enter
3 >>> set3 = set1 & set2 Enter
4 >>> set3 Enter
5 {3, 4}
6 >>>

Finding the Difference of Sets


The difference of set1 and set2 is the elements that appear in set1 but do not appear in
set2. In Python, you can call the difference method to get the difference of two sets.
Here is the general format:
set1.difference(set2)

In the general format, set1 and set2 are sets. The method returns a set that contains the ele-
ments that are found in set1 but not in set2. The following interactive session demonstrates:
1 >>> set1 = set([1, 2, 3, 4]) Enter
2 >>> set2 = set([3, 4, 5, 6]) Enter
3 >>> set3 = set1.difference(set2) Enter
4 >>> set3 Enter
5 {1, 2}
6 >>>
500 Chapter 9   Dictionaries and Sets

You can also use the − operator to find the difference of two sets. Here is the general format
of an expression using the − operator with two sets:
set1 − set2

In the general format, set1 and set2 are sets. The expression returns a set that contains
the elements that are found in set1 but not in set2. The following interactive session
demonstrates:
1 >>> set1 = set([1, 2, 3, 4]) Enter
2 >>> set2 = set([3, 4, 5, 6]) Enter
3 >>> set3 = set1 − set2 Enter
4 >>> set3 Enter
5 {1, 2}
6 >>>

Finding the Symmetric Difference of Sets


The symmetric difference of two sets is a set that contains the elements that are not shared
by the sets. In other words, it is the elements that are in one set but not in both. In Python,
you can call the symmetric_difference method to get the symmetric difference of two
sets. Here is the general format:
set1.symmetric_difference(set2)

In the general format, set1 and set2 are sets. The method returns a set that contains the
elements that are found in either set1 or set2 but not both sets. The following interactive
session demonstrates:
1 >>> set1 = set([1, 2, 3, 4]) Enter
2 >>> set2 = set([3, 4, 5, 6]) Enter
3 >>> set3 = set1.symmetric_difference(set2) Enter
4 >>> set3 Enter
5 {1, 2, 5, 6}
6 >>>

You can also use the ˆ operator to find the symmetric difference of two sets. Here is the
general format of an expression using the ˆ operator with two sets:
set1 ˆ set2

In the general format, set1 and set2 are sets. The expression returns a set that contains the
elements that are found in either set1 or set2, but not both sets. The following interactive
session demonstrates:
1 >>> set1 = set([1, 2, 3, 4]) Enter
2 >>> set2 = set([3, 4, 5, 6]) Enter
3 >>> set3 = set1 ˆ set2 Enter
4 >>> set3 Enter
5 {1, 2, 5, 6}
6 >>>
9.2 Sets 501

Finding Subsets and Supersets


Suppose you have two sets, and one of those sets contains all of the elements of the other
set. Here is an example:
set1 = set([1, 2, 3, 4])
set2 = set([2, 3])

In this example, set1 contains all the elements of set2, which means that set2 is a subset
of set1. It also means that set1 is a superset of set2. In Python, you can call the issubset
method to determine whether one set is a subset of another. Here is the general format:
set2.issubset(set1)

In the general format, set1 and set2 are sets. The method returns True if set2 is a subset
of set1. Otherwise, it returns False. You can call the issuperset method to determine
whether one set is a superset of another. Here is the general format:
set1.issuperset(set2)

In the general format, set1 and set2 are sets. The method returns True if set1 is a super-
set of set2. Otherwise, it returns False. The following interactive session demonstrates:
1 >>> set1 = set([1, 2, 3, 4]) Enter
2 >>> set2 = set([2, 3]) Enter
3 >>> set2.issubset(set1) Enter
4 True
5 >>> set1.issuperset(set2) Enter
6 True
7 >>>

You can also use the <= operator to determine whether one set is a subset of another and
the >= operator to determine whether one set is a superset of another. Here is the general
format of an expression using the <= operator with two sets:
set2 <= set1

In the general format, set1 and set2 are sets. The expression returns True if set2 is a
subset of set1. Otherwise, it returns False. Here is the general format of an expression
using the >= operator with two sets:
set1 >= set2

In the general format, set1 and set2 are sets. The expression returns True if set1
is a subset of set2. Otherwise, it returns False. The following interactive session
demonstrates:
1 >>> set1 = set([1, 2, 3, 4]) Enter
2 >>> set2 = set([2, 3]) Enter
3 >>> set2 <= set1 Enter
4 True
5 >>> set1 >= set2 Enter
6 True
7 >>> set1 <= set2 Enter

8 False
502 Chapter 9   Dictionaries and Sets

In the Spotlight:
Set Operations
In this section, you will look at Program 9-3, which demonstrates various set operations.
The program creates two sets: one that holds the names of students on the baseball team,
and another that holds the names of students on the basketball team. The program then
performs the following operations:
• It finds the intersection of the sets to display the names of students who play both
sports.
• It finds the union of the sets to display the names of students who play either sport.
• It finds the difference of the baseball and basketball sets to display the names of stu-
dents who play baseball but not basketball.
• It finds the difference of the basketball and baseball (basketball – baseball) sets to
display the names of students who play basketball but not baseball. It also finds the
difference of the baseball and basketball (baseball – basketball) sets to display the
names of students who play baseball but not basketball.
• It finds the symmetric difference of the basketball and baseball sets to display the
names of students who play one sport but not both.

Program 9-3 (sets.py)

1 # This program demonstrates various set operations.


2 baseball = set(['Jodi', 'Carmen', 'Aida', 'Alicia'])
3 basketball = set(['Eva', 'Carmen', 'Alicia', 'Sarah'])
4
5 # Display members of the baseball set.
6 print('The following students are on the baseball team:')
7 for name in baseball:
8 print(name)
9
10 # Display members of the basketball set.
11 print()
12 print('The following students are on the basketball team:')
13 for name in basketball:
14 print(name)
15
16 # Demonstrate intersection
17 print()
18 print('The following students play both baseball and basketball:')
19 for name in baseball.intersection(basketball):
20 print(name)
21
22 # Demonstrate union
23 print()
24 print('The following students play either baseball or basketball:')
9.2 Sets 503

25 for name in baseball.union(basketball):


26 print(name)
27
28 # Demonstrate difference of baseball and basketball
29 print()
30 print('The following students play baseball, but not basketball:')
31 for name in baseball.difference(basketball):
32 print(name)
33
34 # Demonstrate difference of basketball and baseball
35 print()
36 print('The following students play basketball, but not baseball:')
37 for name in basketball.difference(baseball):
38 print(name)
39
40 # Demonstrate symmetric difference
41 print()
42 print('The following students play one sport, but not both:')
43 for name in baseball.symmetric_difference(basketball):
44 print(name)

Program Output
The following students are on the baseball team:
Jodi
Aida
Carmen
Alicia

The following students are on the basketball team:


Sarah
Eva
Alicia
Carmen

The following students play both baseball and basketball:


Alicia
Carmen

The following students play either baseball or basketball:


Sarah
Alicia
Jodi
Eva
Aida
Carmen

The following students play baseball but not basketball:


Jodi
Aida
(program output continues)
504 Chapter 9   Dictionaries and Sets

Program Output (continued)


The following students play basketball but not baseball:
Sarah
Eva

The following students play one sport but not both:


Sarah
Aida
Jodi
Eva

Set Comprehensions
A set comprehension is an expression that reads a sequence of input elements and uses
those input elements to produce a set. Set comprehensions work like list comprehensions,
which were discussed in Chapter 7. In fact, set comprehensions are written just like list
comprehensions, except that a set comprehension is enclosed in curly braces ({}), and list
comprehensions are enclosed in square brackets ([]).
Let’s look at some examples. Suppose we have the following set:
set1 = set([1, 2, 3, 4, 5])

The following statement uses a set comprehension to make a copy of the set:
set2 = {item for item in set1}

Let’s look at another example. The following code creates a set of numbers and then creates
a second set that contains the squares of all the numbers in the first set:
set1 = set([1, 2, 3, 4, 5])
set2 = {item**2 for item in set1}

You can also use an if clause with a set comprehension. For example, suppose a set con-
tains integers and you want to make a second set that contains only the integers from the
first set that are less than 10. The following code shows how this can be done with a set
comprehension:
set1 = set([1, 20, 2, 40, 3, 50])
set2 = {item for item in set1 if item < 10}

After this code executes, set2 will contain the following values:
{1, 2, 3}

Checkpoint
9.16 Are the elements of a set ordered or unordered?
9.17 Does a set allow you to store duplicate elements?
9.18 How do you create an empty set?
9.19 After the following statement executes, what elements will be stored in the
myset set?
myset = set('Jupiter')
Classes and Object-
CHAPTER

10 Oriented Programming

TOPICS
10.1 Procedural and Object-Oriented 10.3 Working with Instances
Programming 10.4 Techniques for Designing Classes
10.2 Classes

10.1 Procedural and Object-Oriented Programming

CONCEPT: Procedural programming is a method of writing software. It is a


­programming practice centered on the procedures or actions that take
place in a program. Object-oriented programming is centered on objects.
Objects are created from abstract data types that encapsulate data and
functions together.

There are primarily two methods of programming in use today: procedural and object-
oriented. The earliest programming languages were procedural, meaning a program was
made of one or more procedures. You can think of a procedure simply as a function that
performs a specific task such as gathering input from the user, performing calculations,
reading or writing files, displaying output, and so on. The programs that you have written
so far have been procedural in nature.
Typically, procedures operate on data items that are separate from the procedures. In a
procedural program, the data items are commonly passed from one procedure to another.
As you might imagine, the focus of procedural programming is on the creation of pro-
cedures that operate on the program’s data. The separation of data and the code that
operates on the data can lead to problems, however, as the program becomes larger and
more complex.
For example, suppose you are part of a programming team that has written an extensive
customer database program. The program was initially designed so a customer’s name,

521
522 Chapter 10   Classes and Object-Oriented Programming

address, and phone number were referenced by three variables. Your job was to design
several functions that accept those three variables as arguments and perform operations
on them. The software has been operating successfully for some time, but your team has
been asked to update it by adding several new features. During the revision process, the
senior programmer informs you that the customer’s name, address, and phone number
will no longer be stored in variables. Instead, they will be stored in a list. This means
you will have to modify all of the functions that you have designed so they accept and
work with a list instead of the three variables. Making these extensive modifications
not only is a great deal of work, but also opens the opportunity for errors to appear in
your code.
Whereas procedural programming is centered on creating procedures (functions), object-
oriented programming (OOP) is centered on creating objects. An object is a software
entity that contains both data and procedures. The data contained in an object is known as
the object’s data attributes. An object’s data attributes are simply variables that reference
data. The procedures that an object performs are known as methods. An object’s methods
are functions that perform operations on the object’s data attributes. The object is, con-
ceptually, a self-contained unit that consists of data attributes and methods that operate
on the data attributes. This is illustrated in Figure 10-1.

Figure 10-1 An object contains data attributes and methods

Object
Data attributes

Methods that operate


on the data attributes

OOP addresses the problem of code and data separation through encapsulation and data
hiding. Encapsulation refers to the combining of data and code into a single object. Data
hiding refers to an object’s ability to hide its data attributes from code that is outside the
object. Only the object’s methods may directly access and make changes to the object’s data
attributes.
An object typically hides its data, but allows outside code to access its methods. As shown
in Figure 10-2, the object’s methods provide programming statements outside the object
with indirect access to the object’s data attributes.
10.1 Procedural and Object-Oriented Programming 523

Figure 10-2 Code outside the object interacts with the object’s methods

Object
Data attributes

Code
outside the
object

Methods that operate


on the data attributes

When an object’s data attributes are hidden from outside code, and access to the data attri-
butes is restricted to the object’s methods, the data attributes are protected from accidental
corruption. In addition, the code outside the object does not need to know about the format
or internal structure of the object’s data. The code only needs to interact with the object’s
methods. When a programmer changes the structure of an object’s internal data attributes,
he or she also modifies the object’s methods so they may properly operate on the data. The
way in which outside code interacts with the methods, however, does not change.

Object Reusability
In addition to solving the problems of code and data separation, the use of OOP has also
been encouraged by the trend of object reusability. An object is not a stand-alone program,
but is used by programs that need its services. For example, Sharon is a programmer who
has developed a set of objects for rendering 3D images. She is a math whiz and knows a
lot about computer graphics, so her objects are coded to perform all of the necessary 3D
mathematical operations and handle the computer’s video hardware. Tom, who is writing a
program for an architectural firm, needs his application to display 3D images of buildings.
Because he is working under a tight deadline and does not possess a great deal of knowl-
edge about computer graphics, he can use Sharon’s objects to perform the 3D rendering
(for a small fee, of course!).

An Everyday Example of an Object


Imagine that your alarm clock is actually a software object. If it were, it would have the
following data attributes:
• current_second (a value in the range of 0–59)
• current_minute (a value in the range of 0–59)
• current_hour (a value in the range of 1–12)
• alarm_time (a valid hour and minute)
• alarm_is_set (True or False)
524 Chapter 10   Classes and Object-Oriented Programming

As you can see, the data attributes are merely values that define the state in which the
alarm clock is currently. You, the user of the alarm clock object, cannot directly manipu-
late these data attributes because they are private. To change a data attribute’s value, you
must use one of the object’s methods. The following are some of the alarm clock object’s
methods:
• set_time
• set_alarm_time
• set_alarm_on
• set_alarm_off

Each method manipulates one or more of the data attributes. For example, the set_time
method allows you to set the alarm clock’s time. You activate the method by pressing a
button on top of the clock. By using another button, you can activate the set_alarm_time
method.
In addition, another button allows you to execute the set_alarm_on and set_alarm_off
methods. Notice all of these methods can be activated by you, who are outside the alarm
clock. Methods that can be accessed by entities outside the object are known as public
methods.
The alarm clock also has private methods, which are part of the object’s private, internal
workings. External entities (such as you, the user of the alarm clock) do not have direct
access to the alarm clock’s private methods. The object is designed to execute these methods
automatically and hide the details from you. The following are the alarm clock object’s
private methods:
• increment_current_second
• increment_current_minute
• increment_current_hour
• sound_alarm

Every second the increment_current_second method executes. This changes the value
of the current_second data attribute. If the current_second data attribute is set to
59 when this method executes, the method is programmed to reset current_­second
to 0, and then cause the increment_current_minute method to execute. This method
adds 1 to the current_minute data attribute, unless it is set to 59. In that case, it resets
­ urrent_minute to 0 and causes the increment_current_hour method to execute. The
c
increment_current_minute method compares the new time to the alarm_time. If the
two times match and the alarm is turned on, the sound_alarm method is executed.

Checkpoint
10.1 What is an object?
10.2 What is encapsulation?
10.3 Why is an object’s internal data usually hidden from outside code?
10.4 What are public methods? What are private methods?
10.2 Classes 525

10.2 Classes

CONCEPT: A class is code that specifies the data attributes and methods for a
particular type of object.

Now, let’s discuss how objects are created in software. Before an object can be created, it
must be designed by a programmer. The programmer determines the data attributes and
VideoNote
Classes and methods that are necessary, then creates a class. A class is code that specifies the data
Objects
attributes and methods of a particular type of object. Think of a class as a “blueprint”
from which objects may be created. It serves a similar purpose as the blueprint for a house.
The blueprint itself is not a house, but is a detailed description of a house. When we use
the blueprint to build an actual house, we could say we are building an instance of the
house described by the blueprint. If we so desire, we can build several identical houses
from the same blueprint. Each house is a separate instance of the house described by the
blueprint. This idea is illustrated in Figure 10-3.

Figure 10-3 A blueprint and houses built from the blueprint

Blueprint that describes a house

Instances of the house described by the blueprint

Another way of thinking about the difference between a class and an object is to think of
the difference between a cookie cutter and a cookie. While a cookie cutter itself is not a
cookie, it describes a cookie. The cookie cutter can be used to make one cookie, or several
cookies. Think of a class as a cookie cutter, and the objects created from the class as
cookies.
So, a class is a description of an object’s characteristics. When the program is running, it
can use the class to create, in memory, as many objects of a specific type as needed. Each
object that is created from a class is called an instance of the class.
526 Chapter 10   Classes and Object-Oriented Programming

For example, Jessica is an entomologist (someone who studies insects), and she also enjoys
writing computer programs. She designs a program to catalog different types of insects. As
part of the program, she creates a class named Insect, which specifies characteristics that
are common to all types of insects. The Insect class is a specification from which objects
may be created. Next, she writes programming statements that create an object named
housefly, which is an instance of the Insect class. The housefly object is an entity that
occupies computer memory and stores data about a housefly. It has the data attributes and
methods specified by the Insect class. Then she writes programming statements that create
an object named mosquito. The mosquito object is also an instance of the Insect class.
It has its own area in memory and stores data about a mosquito. Although the housefly
and mosquito objects are separate entities in the computer’s memory, they were both cre-
ated from the Insect class. This means that each of the objects has the data attributes and
methods described by the Insect class. This is illustrated in Figure 10-4.

Figure 10-4 The housefly and mosquito objects are instances of the Insect class

The housefly object is an


instance of the Insect class. It
housefly has the data attributes and methods
object described by the Insect class.
The Insect class describes
the data attributes and Insect
methods that a particular class
type of object may have. The mosquito object is an
mosquito
instance of the Insect class. It
object
has the data attributes and methods
described by the Insect class.

Class Definitions
To create a class, you write a class definition. A class definition is a set of statements that
define a class’s methods and data attributes. Let’s look at a simple example. Suppose we are
writing a program to simulate the tossing of a coin. In the program, we need to repeatedly
toss the coin and each time determine whether it landed heads up or tails up. Taking an
object-oriented approach, we will write a class named Coin that can perform the behaviors
of the coin.
Program 10-1 shows the class definition, which we will explain shortly. Note this is not a
complete program. We will add to it as we go along.

Program 10-1 (Coin class, not a complete program)

1 import random
2
3 # The Coin class simulates a coin that can
4 # be flipped.
5
6 class Coin:
7
8 # The _ _init_ _ method initializes the
10.2 Classes 527

9 # sideup data attribute with 'Heads'.


10
11 def _ _init_ _(self):
12 self.sideup = 'Heads'
13
14 # The toss method generates a random number
15 # in the range of 0 through 1. If the number
16 # is 0, then sideup is set to 'Heads'.
17 # Otherwise, sideup is set to 'Tails'.
18
19 def toss(self):
20 if random.randint(0, 1) == 0:
21 self.sideup = 'Heads'
22 else:
23 self.sideup = 'Tails'
24
25 # The get_sideup method returns the value
26 # referenced by sideup.
27
28 def get_sideup(self):
29 return self.sideup

In line 1, we import the random module. This is necessary because we use the randint
function to generate a random number. Line 6 is the beginning of the class definition. It
begins with the keyword class, followed by the class name, which is Coin, followed by
a colon.
The same rules that apply to variable names also apply to class names. However, notice
that we started the class name, Coin, with an uppercase letter. This is not a requirement,
but it is a widely used convention among programmers. This helps to easily distinguish class
names from variable names when reading code.
The Coin class has three methods:
• The _ _init_ _ method appears in lines 11 through 12.
• The toss method appears in lines 19 through 23.
• The get_sideup method appears in lines 28 through 29.
Except for the fact that they appear inside a class, notice these method definitions look like
any other function definition in Python. They start with a header line, which is followed by
an indented block of statements.
Take a closer look at the header for each of the method definitions (lines 11, 19, and 28)
and notice each method has a parameter variable named self:
Line 11: def _ _init_ _(self):
Line 19: def toss(self):
Line 28: def get_sideup(self):
528 Chapter 10   Classes and Object-Oriented Programming

The self parameter1 is required in every method of a class. Recall from our earlier discus-
sion on object-oriented programming that a method operates on a specific object’s data
attributes. When a method executes, it must have a way of knowing which object’s data
attributes it is supposed to operate on. That’s where the self parameter comes in. When
a method is called, Python makes the self parameter reference the specific object that the
method is supposed to operate on.
Let’s look at each of the methods. The first method, which is named _ _init_ _, is defined
in lines 11 through 12:
def _ _init_ _(self):
self.sideup = 'Heads'

Most Python classes have a special method named _ _init_ _, which is automatically
executed when an instance of the class is created in memory. The _ _init_ _ method is
commonly known as an initializer method because it initializes the object’s data attributes.
(The name of the method starts with two underscore characters, followed by the word
init, followed by two more underscore characters.)

Immediately after an object is created in memory, the _ _init_ _ method executes, and
the self parameter is automatically assigned the object that was just created. Inside the
method, the statement in line 12 executes:
self.sideup = 'Heads'

This statement assigns the string 'Heads' to the sideup data attribute belonging to
the object that was just created. As a result of this _ _init_ _ method, each object we
create from the Coin class will initially have a sideup attribute that is set to 'Heads'.

NOTE: The _ _init_ _ method is usually the first method inside a class definition.

The toss method appears in lines 19 through 23:


def toss(self):
if random.randint(0, 1) == 0:
self.sideup = 'Heads'
else:
self.sideup = 'Tails'

This method also has the required self parameter variable. When the toss method is
called, self will automatically reference the object on which the method is to operate.
The toss method simulates the tossing of the coin. When the method is called, the if
statement in line 20 calls the random.randint function to get a random integer in the
range of 0 through 1. If the number is 0, then the statement in line 21 assigns 'Heads'
to self.sideup. Otherwise, the statement in line 23 assigns 'Tails' to self.sideup.

1
The parameter must be present in a method. You are not required to name it self, but this is
strongly recommended to conform with standard practice.
10.2 Classes 529

The get_sideup method appears in lines 28 through 29:


def get_sideup(self):
return self.sideup

Once again, the method has the required self parameter variable. This method simply
returns the value of self.sideup. We call this method any time we want to know which
side of the coin is facing up.
To demonstrate the Coin class, we need to write a complete program that uses it to create
an object. Program 10-2 shows an example. The Coin class definition appears in lines 6
through 29. The program has a main function, which appears in lines 32 through 44.

Program 10-2 (coin_demo1.py)

1 import random
2
3 # The Coin class simulates a coin that can
4 # be flipped.
5
6 class Coin:
7
8 # The _ _init_ _ method initializes the
9 # sideup data attribute with 'Heads'.
10
11 def _ _init_ _(self):
12 self.sideup = 'Heads'
13
14 # The toss method generates a random number
15 # in the range of 0 through 1. If the number
16 # is 0, then sideup is set to 'Heads'.
17 # Otherwise, sideup is set to 'Tails'.
18
19 def toss(self):
20 if random.randint(0, 1) == 0:
21 self.sideup = 'Heads'
22 else:
23 self.sideup = 'Tails'
24
25 # The get_sideup method returns the value
26 # referenced by sideup.
27
28 def get_sideup(self):
29 return self.sideup
30
31 # The main function.
32 def main():
33 # Create an object from the Coin class.
(program continues)
530 Chapter 10   Classes and Object-Oriented Programming

Program 10-2 (continued)

34 my_coin = Coin()
35
36 # Display the side of the coin that is facing up.
37 print('This side is up:', my_coin.get_sideup())
38
39 # Toss the coin.
40 print('I am tossing the coin ...')
41 my_coin.toss()
42
43 # Display the side of the coin that is facing up.
44 print('This side is up:', my_coin.get_sideup())
45
46 # Call the main function.
47 47 if _ _name_ _ == '_ _main_ _':
48 main()

Program Output
This side is up: Heads
I am tossing the coin ...
This side is up: Tails

Program Output
This side is up: Heads
I am tossing the coin ...
This side is up: Heads

Program Output
This side is up: Heads
I am tossing the coin ...
This side is up: Tails

Take a closer look at the statement in line 34:


my_coin = Coin()

The expression Coin() that appears on the right side of the = operator causes two things
to happen:
1. An object is created in memory from the Coin class.
2. The Coin class’s _ _init_ _ method is executed, and the self parameter is automati-
cally set to the object that was just created. As a result, that object’s sideup attribute
is assigned the string 'Heads'.
Figure 10-5 illustrates these steps.
10.2 Classes 531

Figure 10-5 Actions caused by the Coin() expression


A Coin object
An object is created in memory
1
from the Coin class.

The Coin class's _ _init_ _


method is called, and the self def _ _init_ _(self):
2
parameter is set to the newly self.sideup = 'Heads'
created object

A Coin object
After these steps take place,
a Coin object will exist with its sideup 'Heads'
sideup attribute set to 'Heads'.

After this, the = operator assigns the Coin object that was just created to the my_coin
variable. Figure 10-6 shows that after the statement in line 12 executes, the my_coin
variable will reference a Coin object, and that object’s sideup attribute will be assigned
the string 'Heads'.

Figure 10-6 The my_coin variable references a Coin object

A Coin object

my_coin sideup 'Heads'

The next statement to execute is line 37:


print('This side is up:', my_coin.get_sideup())

This statement prints a message indicating the side of the coin that is facing up. Notice
the following expression appears in the statement:
my_coin.get_sideup()

This expression uses the object referenced by my_coin to call the get_sideup method.
When the method executes, the self parameter will reference the my_coin object. As a
result, the method returns the string 'Heads'.
Notice we did not have to pass an argument to the sideup method, despite the fact that
it has the self parameter variable. When a method is called, Python automatically passes
a reference to the calling object into the method’s first parameter. As a result, the self
parameter will automatically reference the object on which the method is to operate.
532 Chapter 10   Classes and Object-Oriented Programming

Lines 40 and 41 are the next statements to execute:


print('I am tossing the coin ...')
my_coin.toss()

The statement in line 41 uses the object referenced by my_coin to call the toss method.
When the method executes, the self parameter will reference the my_coin object. The
method will randomly generate a number, then use that number to change the value of the
object’s sideup attribute.
Line 44 executes next. This statement calls my_coin.get_sideup() to display the side of
the coin that is facing up.

Hiding Attributes
Earlier in this chapter, we mentioned that an object’s data attributes should be private,
so that only the object’s methods can directly access them. This protects the object’s data
attributes from accidental corruption. However, in the Coin class that was shown in the
previous example, the sideup attribute is not private. It can be directly accessed by state-
ments that are not in a Coin class method. Program 10-3 shows an example. Note lines 1
through 30 are not shown to conserve space. Those lines contain the Coin class, and they
are the same as lines 1 through 30 in Program 10-2.

Program 10-3 (coin_demo2.py)

Lines 1 through 30 are omitted. These lines are the same as lines 1 through 30 in Program 10-2.
31 # The main function.
32 def main():
33 # Create an object from the Coin class.
34 my_coin = Coin()
35
36 # Display the side of the coin that is facing up.
37 print('This side is up:', my_coin.get_sideup())
38
39 # Toss the coin.
40 print('I am tossing the coin ...')
41 my_coin.toss()
42
43 # But now I'm going to cheat! I'm going to
44 # directly change the value of the object's
45 # sideup attribute to 'Heads'.
46 my_coin.sideup = 'Heads'
47
48 # Display the side of the coin that is facing up.
49 print('This side is up:', my_coin.get_sideup())
50
51 # Call the main function.
52 if _ _name_ _ == '_ _main_ _':
53       main()
10.2 Classes 533

Program Output
This side is up: Heads
I am tossing the coin ...
This side is up: Heads

Program Output
This side is up: Heads
I am tossing the coin ...
This side is up: Heads

Program Output
This side is up: Heads
I am tossing the coin ...
This side is up: Heads

Line 34 creates a Coin object in memory and assigns it to the my_coin variable. The state-
ment in line 37 displays the side of the coin that is facing up, then line 41 calls the object’s
toss method. Then, the statement in line 46 directly assigns the string 'Heads' to the obj­
ect’s sideup attribute:
my_coin.sideup = 'Heads'

Regardless of the outcome of the toss method, this statement will change the my_coin
object’s sideup attribute to 'Heads'. As you can see from the three sample runs of the
program, the coin always lands heads up!
If we truly want to simulate a coin that is being tossed, then we don’t want code outside the
class to be able to change the result of the toss method. To prevent this from happening,
we need to make the sideup attribute private. In Python, you can hide an attribute by start-
ing its name with two underscore characters. If we change the name of the sideup attribute
to _ _sideup, then code outside the Coin class will not be able to access it. Program 10-4
shows a new version of the Coin class, with this change made.

Program 10-4 (coin_demo3.py)

1 import random
2
3 # The Coin class simulates a coin that can
4 # be flipped.
5
6 class Coin:
7
8 # The _ _init_ _ method initializes the
9 # _ _sideup data attribute with ‘Heads’.
10
11 def _ _init_ _(self):
12 self._ _sideup = 'Heads'
13
(program continues)
534 Chapter 10   Classes and Object-Oriented Programming

Program 10-4 (continued)

14 # The toss method generates a random number


15 # in the range of 0 through 1. If the number
16 # is 0, then sideup is set to 'Heads'.
17 # Otherwise, sideup is set to 'Tails'.
18
19 def toss(self):
20 if random.randint(0, 1) == 0:
21 self._ _sideup = 'Heads'
22 else:
23 self._ _sideup = 'Tails'
24
25 # The get_sideup method returns the value
26 # referenced by sideup.
27
28 def get_sideup(self):
29 return self._ _sideup
30
31 # The main function.
32 def main():
33 # Create an object from the Coin class.
34 my_coin = Coin()
35
36 # Display the side of the coin that is facing up.
37 print('This side is up:', my_coin.get_sideup())
38
39 # Toss the coin.
40 print('I am going to toss the coin ten times:')
41 for count in range(10):
42 my_coin.toss()
43 print(my_coin.get_sideup())
44
45 # Call the main function.
46 46 if _ _name_ _ == '_ _main_ _':
47      main()

Program Output
This side is up: Heads
I am going to toss the coin ten times:
Tails
Heads
Heads
Tails
Tails
10.2 Classes 535

Tails
Tails
Tails
Heads
Heads

Storing Classes in Modules


The programs you have seen so far in this chapter have the Coin class definition in the same
file as the programming statements that use the Coin class. This approach works fine with
small programs that use only one or two classes. As programs use more classes, however,
the need to organize those classes becomes greater.
Programmers commonly organize their class definitions by storing them in modules. Then
the modules can be imported into any programs that need to use the classes they contain. For
example, suppose we decide to store the Coin class in a module named coin. Program 10-5
shows the contents of the coin.py file. Then, when we need to use the Coin class in a pro-
gram, we can import the coin module. This is demonstrated in Program 10-6.

Program 10-5 (coin.py)

1 import random
2
3 # The Coin class simulates a coin that can
4 # be flipped.
5
6 class Coin:
7
8 # The _ _init_ _ method initializes the
9 # _ _sideup data attribute with 'Heads'.
10
11 def _ _init_ _(self):
12 self._ _sideup = 'Heads'
13
14 # The toss method generates a random number
15 # in the range of 0 through 1. If the number
16 # is 0, then sideup is set to 'Heads'.
17 # Otherwise, sideup is set to 'Tails'.
18
19 def toss(self):
20 if random.randint(0, 1) == 0:
21 self._ _sideup = 'Heads'
22 else:
23 self._ _sideup = 'Tails'
24
25 # The get_sideup method returns the value
(program continues)
536 Chapter 10   Classes and Object-Oriented Programming

Program 10-5 (continued)

26 # referenced by sideup.
27
28 def get_sideup(self):
29 return self._ _sideup

Program 10-6 (coin_demo4.py)

1 # This program imports the coin module and


2 # creates an instance of the Coin class.
3
4 import coin
5
6 def main():
7 # Create an object from the Coin class.
8 my_coin = coin.Coin()
9
10 # Display the side of the coin that is facing up.
11 print('This side is up:', my_coin.get_sideup())
12
13 # Toss the coin.
14 print('I am going to toss the coin ten times:')
15 for count in range(10):
16 my_coin.toss()
17 print(my_coin.get_sideup())
18
19 # Call the main function.
20 if _ _name_ _ == '_ _main_ _':
21 main()

Program Output
This side is up: Heads
I am going to toss the coin ten times:
Tails
Tails
Heads
Tails
Heads
Heads
Tails
Heads
Tails
Tails
10.2 Classes 537

Line 4 imports the coin module. Notice in line 8, we had to qualify the name of the Coin
class by prefixing it with the name of the module, followed by a dot:
my_coin = coin.Coin()

The BankAccount Class


Let’s look at another example. Program 10-7 shows a BankAccount class, stored in a
module named bankaccount. Objects that are created from this class will simulate bank
accounts, allowing us to have a starting balance, make deposits, make withdrawals, and
get the current balance.

Program 10-7 (bankaccount.py)

1 # The BankAccount class simulates a bank account.


2
3 class BankAccount:
4
5 # The _ _init_ _ method accepts an argument for
6 # the account's balance. It is assigned to
7 # the _ _balance attribute.
8
9 def _ _init_ _(self, bal):
10 self._ _balance = bal
11
12 # The deposit method makes a deposit into the
13 # account.
14
15 def deposit(self, amount):
16 self._ _balance += amount
17
18 # The withdraw method withdraws an amount
19 # from the account.
20
21 def withdraw(self, amount):
22 if self._ _balance >= amount:
23 self._ _balance −= amount
24 else:
25 print('Error: Insufficient funds')
26
27 # The get_balance method returns the
28 # account balance.
29
30 def get_balance(self):
31 return self._ _balance

Notice the _ _init_ _ method has two parameter variables: self and bal. The bal param-
eter will accept the account’s starting balance as an argument. In line 10, the bal parameter
amount is assigned to the object’s _ _balance attribute.
538 Chapter 10   Classes and Object-Oriented Programming

The deposit method is in lines 15 through 16. This method has two parameter variables:
self and amount. When the method is called, the amount that is to be deposited into the
account is passed into the amount parameter. The value of the parameter is then added to
the _ _balance attribute in line 16.
The withdraw method is in lines 21 through 25. This method has two parameter variables:
self and amount. When the method is called, the amount that is to be withdrawn from
the account is passed into the amount parameter. The if statement that begins in line 22
determines whether there is enough in the account balance to make the withdrawal. If so,
amount is subtracted from _ _balance in line 23. Otherwise, line 25 displays the message
'Error: Insufficient funds'.

The get_balance method is in lines 30 through 31. This method returns the value of the
_ _balance attribute.

Program 10-8 demonstrates how to use the class.

Program 10-8 (account_test.py)

1 # This program demonstrates the BankAccount class.


2
3 import bankaccount
4
5 def main():
6 # Get the starting balance.
7 start_bal = float(input('Enter your starting balance: '))
8
9 # Create a BankAccount object.
10 savings = bankaccount.BankAccount(start_bal)
11
12 # Deposit the user's paycheck.
13 pay = float(input('How much were you paid this week? '))
14 print('I will deposit that into your account.')
15 savings.deposit(pay)
16
17 # Display the balance.
18 print(f'Your account balance is ${savings.get_balance():,.2f}.')
19
20 # Get the amount to withdraw.
21 cash = float(input(‘How much would you like to withdraw? '))
22 print('I will withdraw that from your account.')
23 savings.withdraw(cash)
24
25 # Display the balance.
26 print(f'Your account balance is ${savings.get_balance():,.2f}.')
27
28 # Call the main function.
29 if _ _name_ _ == '_ _main_ _':
30 main()
10.2 Classes 539

Program Output (with input shown in bold)


Enter your starting balance: 1000.00 Enter
How much were you paid this week? 500.00 Enter
I will deposit that into your account.
Your account balance is $1,500.00
How much would you like to withdraw? 1200.00 Enter
I will withdraw that from your account.
Your account balance is $300.00

Program Output (with input shown in bold)


Enter your starting balance: 1000.00 Enter
How much were you paid this week? 500.00 Enter
I will deposit that into your account.
Your account balance is $1,500.00
How much would you like to withdraw? 2000.00 Enter
I will withdraw that from your account.
Error: Insufficient funds
Your account balance is $1,500.00

Line 7 gets the starting account balance from the user and assigns it to the start_bal vari-
able. Line 10 creates an instance of the BankAccount class and assigns it to the savings
variable. Take a closer look at the statement:
savings = bankaccount.BankAccount(start_bal)

Notice the start_bal variable is listed inside the parentheses. This causes the start_bal
variable to be passed as an argument to the _ _init_ _ method. In the _ _init_ _ method, it
will be passed into the bal parameter.
Line 13 gets the amount of the user’s pay and assigns it to the pay variable. In line 15,
the savings.deposit method is called, passing the pay variable as an argument. In the
deposit method, it will be passed into the amount parameter.

The statement in line 18 displays the account balance. Notice that we are using an f-string
to call the savings.get_balance method. The value that is returned from the method is
formatted to appear as a dollar amount.
Line 21 gets the amount that the user wants to withdraw and assigns it to the cash vari-
able. In line 23 the savings.withdraw method is called, passing the cash variable as
an argument. In the withdraw method, it will be passed into the amount parameter. The
statement in line 26 displays the ending account balance.

The _ _str_ _ Method


Quite often, we need to display a message that indicates an object’s state. An object’s
state is simply the values of the object’s attributes at any given moment. For example,
recall the BankAccount class has one data attribute: _ _balance. At any given moment,
a BankAccount object’s _ _balance attribute will reference some value. The value of the
540 Chapter 10   Classes and Object-Oriented Programming

_ _balance attribute represents the object’s state at that moment. The following might be
an example of code that displays a BankAccount object’s state:
account = bankaccount.BankAccount(1500.0)
print(f'The balance is ${savings.get_balance():,.2f}'))

The first statement creates a BankAccount object, passing the value 1500.0 to the
_ _init_ _ method. After this statement executes, the account variable will reference the
BankAccount object. The second line displays a formatted string showing the value of
the object’s _ _balance attribute. The output of this statement will look like this:
The balance is $1,500.00

Displaying an object’s state is a common task. It is so common that many program-


mers equip their classes with a method that returns a string containing the object’s state.
In Python, you give this method the special name _ _str_ _. Program 10-9 shows the
BankAccount class with a _ _str_ _ method added to it. The _ _str_ _ method appears in
lines 36 through 37. It returns a string indicating the account balance.

Program 10-9 (bankaccount2.py)

1 # The BankAccount class simulates a bank account.


2
3 class BankAccount:
4
5 # The _ _init_ _ method accepts an argument for
6 # the account's balance. It is assigned to
7 # the _ _balance attribute.
8
9 def _ _init_ _(self, bal):
10 self._ _balance = bal
11
12 # The deposit method makes a deposit into the
13 # account.
14
15 def deposit(self, amount):
16 self._ _balance += amount
17
18 # The withdraw method withdraws an amount
19 # from the account.
20
21 def withdraw(self, amount):
22 if self._ _balance >= amount:
23 self._ _balance −= amount
24 else:
25 print('Error: Insufficient funds')
26
27 # The get_balance method returns the
28 # account balance.
29
30 def get_balance(self):
10.2 Classes 541

31 return self._ _balance


32
33 # The _ _str_ _ method returns a string
34 # indicating the object's state.
35
36 def _ _str_ _(self):
37 return f'The balance is ${self._ _balance:,.2f}'

You do not directly call the _ _str_ _ method. Instead, it is automatically called when you
pass an object as an argument to the print function. Program 10-10 shows an example.

Program 10-10 (account_test2.py)

1 # This program demonstrates the BankAccount class


2 # with the _ _str_ _ method added to it.
3
4 import bankaccount2
5
6 def main():
7 # Get the starting balance.
8 start_bal = float(input('Enter your starting balance: '))
9
10 # Create a BankAccount object.
11 savings = bankaccount2.BankAccount(start_bal)
12
13 # Deposit the user's paycheck.
14 pay = float(input('How much were you paid this week? '))
15 print('I will deposit that into your account.')
16 savings.deposit(pay)
17
18 # Display the balance.
19 print(savings)
20
21 # Get the amount to withdraw.
22 cash = float(input('How much would you like to withdraw? '))
23 print('I will withdraw that from your account.')
24 savings.withdraw(cash)
25
26 # Display the balance.
27 print(savings)
28
29 # Call the main function.
30 if _ _name_ _ == '_ _main_ _':
31 main()

Program Output (with input shown in bold)


Enter your starting balance: 1000.00 Enter
How much were you paid this week? 500.00 Enter
(program output continues)
542 Chapter 10   Classes and Object-Oriented Programming

Program Output (continued)


I will deposit that into your account.
The account balance is $1,500.00
How much would you like to withdraw? 1200.00 Enter
I will withdraw that from your account.
The account balance is $300.00

The name of the object, savings, is passed to the print function in lines 19 and 27. This
causes the BankAccount class’s _ _str_ _ method to be called. The string that is returned
from the _ _str_ _ method is then displayed.
The _ _str_ _ method is also called automatically when an object is passed as an argument
to the built-in str function. Here is an example:
account = bankaccount2.BankAccount(1500.0)
message = str(account)
print(message)

In the second statement, the account object is passed as an argument to the str function.
This causes the BankAccount class’s _ _str_ _ method to be called. The string that is returned
is assigned to the message variable then displayed by the print function in the third line.

Checkpoint
10.5 You hear someone make the following comment: “A blueprint is a design for
a house. A carpenter can use the blueprint to build the house. If the carpenter
wishes, he or she can build several identical houses from the same blueprint.”
Think of this as a metaphor for classes and objects. Does the blueprint represent
a class, or does it represent an object?
10.6 In this chapter, we use the metaphor of a cookie cutter and cookies that are
made from the cookie cutter to describe classes and objects. In this metaphor, are
objects the cookie cutter, or the cookies?
10.7 What is the purpose of the _ _init_ _ method? When does it execute?
10.8 What is the purpose of the self parameter in a method?
10.9 In a Python class, how do you hide an attribute from code outside the class?
10.10 What is the purpose of the _ _str_ _ method?
10.11 How do you call the _ _str_ _ method?

10.3 Working with Instances

CONCEPT: Each instance of a class has its own set of data attributes.

When a method uses the self parameter to create an attribute, the attribute belongs to the
specific object that self references. We call these attributes instance attributes because they
belong to a specific instance of the class.
It is possible to create many instances of the same class in a program. Each instance will
then have its own set of attributes. For example, look at Program 10-11. This program
creates three instances of the Coin class. Each instance has its own _ _sideup attribute.
10.3 Working with Instances 543

Program 10-11 (coin_demo5.py)

1 # This program imports the simulation module and


2 # creates three instances of the Coin class.
3
4 import coin
5
6 def main():
7 # Create three objects from the Coin class.
8 coin1 = coin.Coin()
9 coin2 = coin.Coin()
10 coin3 = coin.Coin()
11
12 # Display the side of each coin that is facing up.
13 print('I have three coins with these sides up:')
14 print(coin1.get_sideup())
15 print(coin2.get_sideup())
16 print(coin3.get_sideup())
17 print()
18
19 # Toss the coin.
20 print('I am tossing all three coins ...')
21 print()
22 coin1.toss()
23 coin2.toss()
24 coin3.toss()
25
26 # Display the side of each coin that is facing up.
27 print('Now here are the sides that are up:')
28 print(coin1.get_sideup())
29 print(coin2.get_sideup())
30 print(coin3.get_sideup())
31 print()
32
33 # Call the main function.
34 if _ _name_ _ == '_ _main_ _':
35 main()

Program Output
I have three coins with these sides up:
Heads
Heads
Heads

I am tossing all three coins ...

Now here are the sides that are up:


Tails
Tails
Heads
544 Chapter 10   Classes and Object-Oriented Programming

In lines 8 through 10, the following statements create three objects, each an instance of the
Coin class:

coin1 = coin.Coin()
coin2 = coin.Coin()
coin3 = coin.Coin()

Figure 10-7 illustrates how the coin1, coin2, and coin3 variables reference the three
objects after these statements execute. Notice each object has its own _ _sideup attribute.
Lines 14 through 16 display the values returned from each object’s get_sideup method.

Figure 10-7 The coin1, coin2, and coin3 variables reference three Coin objects

A Coin object

coin1 _ _sideup 'Heads'

A Coin object

coin2 _ _sideup 'Heads'

A Coin object

coin3 _ _sideup 'Heads'

Then, the statements in lines 22 through 24 call each object’s toss method:
coin1.toss()
coin2.toss()
coin3.toss()

Figure 10-8 shows how these statements changed each object’s _ _sideup attribute in the
program’s sample run.

Figure 10-8 The objects after the toss method


A Coin object

coin1 _ _sideup 'Tails'

A Coin object

coin2 _ _sideup 'Tails'

A Coin object

coin3 _ _sideup 'Heads'


10.3 Working with Instances 545

In the Spotlight:
Creating the CellPhone Class
Wireless Solutions, Inc. is a business that sells cell phones and wireless service. You are a
programmer in the company’s IT department, and your team is designing a program to
manage all of the cell phones that are in inventory. You have been asked to design a class
that represents a cell phone. The data that should be kept as attributes in the class are as
follows:
• The name of the phone’s manufacturer will be assigned to the _ _manufact attribute.
• The phone’s model number will be assigned to the _ _model attribute.
• The phone’s retail price will be assigned to the _ _retail_price attribute.
The class will also have the following methods:
• An _ _init_ _ method that accepts arguments for the manufacturer, model number,
and retail price.
• A set_manufact method that accepts an argument for the manufacturer. This method
will allow us to change the value of the _ _manufact attribute after the object has
been created, if necessary.
• A set_model method that accepts an argument for the model. This method will allow
us to change the value of the _ _model attribute after the object has been created, if
necessary.
• A set_retail_price method that accepts an argument for the retail price. This
method will allow us to change the value of the _ _retail_price attribute after the
object has been created, if necessary.
• A get_manufact method that returns the phone’s manufacturer.
• A get_model method that returns the phone’s model number.
• A get_retail_price method that returns the phone’s retail price.
Program 10-12 shows the class definition. The class is stored in a module named cellphone.

Program 10-12 (cellphone.py)

1 # The CellPhone class holds data about a cell phone.


2
3 class CellPhone:
4
5 # The _ _init_ _ method initializes the attributes.
6
7 def _ _init_ _(self, manufact, model, price):
8 self._ _manufact = manufact
9 self._ _model = model
10 self._ _retail_price = price
11
12 # The set_manufact method accepts an argument for
13 # the phone's manufacturer.
14
15 def set_manufact(self, manufact):
16 self._ _manufact = manufact (program continues)
546 Chapter 10   Classes and Object-Oriented Programming

Program 10-12 (continued)

17
18 # The set_model method accepts an argument for
19 # the phone's model number.
20
21 def set_model(self, model):
22 self._ _model = model
23
24 # The set_retail_price method accepts an argument
25 # for the phone's retail price.
26
27 def set_retail_price(self, price):
28 self._ _retail_price = price
29
30 # The get_manufact method returns the
31 # phone's manufacturer.
32
33 def get_manufact(self):
34 return self._ _manufact
35
36 # The get_model method returns the
37 # phone's model number.
38
39 def get_model(self):
40 return self._ _model
41
42 # The get_retail_price method returns the
43 # phone's retail price.
44
45 def get_retail_price(self):
46 return self._ _retail_price

The CellPhone class will be imported into several programs that your team is develop-
ing. To test the class, you write the code in Program 10-13. This is a simple program that
prompts the user for the phone’s manufacturer, model number, and retail price. An instance
of the CellPhone class is created, and the data is assigned to its attributes.

Program 10-13 (cell_phone_test.py)

1 # This program tests the CellPhone class.


2
3 import cellphone
4
5 def main():
6 # Get the phone data.
7 man = input('Enter the manufacturer: ')
10.3 Working with Instances 547

8 mod = input('Enter the model number: ')


9 retail = float(input('Enter the retail price: '))
10
11 # Create an instance of the CellPhone class.
12 phone = cellphone.CellPhone(man, mod, retail)
13
14 # Display the data that was entered.
15 print('Here is the data that you entered:')
16 print(f'Manufacturer: {phone.get_manufact()}')
17 print(f'Model Number: {phone.get_model()}')
18 print(f'Retail Price: ${phone.get_retail_price():,.2f}')
19
20 # Call the main function.
21 if _ _name_ _ == '_ _main_ _':
22 main()

Program Output (with input shown in bold)


Enter the manufacturer: Acme Electronics Enter
Enter the model number: M1000 Enter
Enter the retail price: 199.99 Enter
Here is the data that you entered:
Manufacturer: Acme Electronics
Model Number: M1000
Retail Price: $199.99

Accessor and Mutator Methods


As mentioned earlier, it is a common practice to make all of a class’s data attributes private,
and to provide public methods for accessing and changing those attributes. This ensures
that the object owning those attributes is in control of all the changes being made to them.
A method that returns a value from a class’s attribute but does not change it is known as an
accessor method. Accessor methods provide a safe way for code outside the class to retrieve
the values of attributes, without exposing the attributes in a way that they could be changed
by the code outside the method. In the CellPhone class that you saw in Program 10-12 (in
the previous In the Spotlight section), the get_manufact, get_model, and get_retail_
price methods are accessor methods.

A method that stores a value in a data attribute or changes the value of a data attribute
in some other way is known as a mutator method. Mutator methods can control the way
that a class’s data attributes are modified. When code outside the class needs to change
the value of an object’s data attribute, it typically calls a mutator and passes the new
value as an argument. If necessary, the mutator can validate the value before it assigns it to
the data attribute. In Program 10-12, the set_manufact, set_model, and set_retail_
price methods are mutator methods.

NOTE: Mutator methods are sometimes called “setters,” and accessor methods are
sometimes called “getters.”
548 Chapter 10   Classes and Object-Oriented Programming

In the Spotlight:
Storing Objects in a List
The CellPhone class you created in the previous In the Spotlight section will be used in a
variety of programs. Many of these programs will store CellPhone objects in lists. To test
the ability to store CellPhone objects in a list, you write the code in Program 10-14. This
program gets the data for five phones from the user, creates five CellPhone objects hold-
ing that data, and stores those objects in a list. It then iterates over the list displaying the
attributes of each object.

Program 10-14 (cell_phone_list.py)

1 # This program creates five CellPhone objects and


2 # stores them in a list.
3
4 import cellphone
5
6 def main():
7 # Get a list of CellPhone objects.
8 phones = make_list()
9
10 # Display the data in the list.
11 print('Here is the data you entered:')
12 display_list(phones)
13
14 # The make_list function gets data from the user
15 # for five phones. The function returns a list
16 # of CellPhone objects containing the data.
17
18 def make_list():
19 # Create an empty list.
20 phone_list = []
21
22 # Add five CellPhone objects to the list.
23 print('Enter data for five phones.')
24 for count in range(1, 6):
25 # Get the phone data.
26 print('Phone number ' + str(count) + ':')
27 man = input('Enter the manufacturer: ')
28 mod = input('Enter the model number: ')
29 retail = float(input('Enter the retail price: '))
30 print()
31
32 # Create a new CellPhone object in memory and
33 # assign it to the phone variable.
34 phone = cellphone.CellPhone(man, mod, retail)
35
36 # Add the object to the list.
37 phone_list.append(phone)
10.3 Working with Instances 549

38
39 # Return the list.
40 return phone_list
41
42 # The display_list function accepts a list containing
43 # CellPhone objects as an argument and displays the
44 # data stored in each object.
45
46 def display_list(phone_list):
47 for item in phone_list:
48 print(item.get_manufact())
49 print(item.get_model())
50 print(item.get_retail_price())
51 print()
52
53 # Call the main function.
54    if _ _name_ _ == '_ _main_ _':
55 main()

Program Output (with input shown in bold)


Enter data for five phones.

Phone number 1:
Enter the manufacturer: Acme Electronics Enter
Enter the model number: M1000 Enter
Enter the retail price: 199.99 Enter

Phone number 2:
Enter the manufacturer: Atlantic Communications Enter
Enter the model number: S2 Enter
Enter the retail price: 149.99 Enter

Phone number 3:
Enter the manufacturer: Wavelength Electronics Enter
Enter the model number: N477 Enter
Enter the retail price: 249.99 Enter

Phone number 4:
Enter the manufacturer: Edison Wireless Enter
Enter the model number: SLX88 Enter
Enter the retail price: 169.99 Enter

Phone number 5:
Enter the manufacturer: Sonic Systems Enter
Enter the model number: X99 Enter
Enter the retail price: 299.99 Enter

Here is the data you entered:


Acme Electronics
M1000
199.99
(program output continues)
550 Chapter 10   Classes and Object-Oriented Programming

Program Output (continued)


Atlantic Communications
S2
149.99

Wavelength Electronics
N477
249.99

Edison Wireless
SLX88
169.99

Sonic Systems
X99
299.99

The make_list function appears in lines 18 through 40. In line 20, an empty list named
phone_list is created. The for loop, which begins in line 24, iterates five times. Each time
the loop iterates, it gets the data for a cell phone from the user (lines 27 through 29), it
creates an instance of the CellPhone class that is initialized with the data (line 34), and it
appends the object to the phone_list list (line 37). Line 40 returns the list.
The display_list function in lines 46 through 51 accepts a list of CellPhone objects as
an argument. The for loop that begins in line 47 iterates over the objects in the list, and
displays the values of each object’s attributes.

Passing Objects as Arguments


When you are developing applications that work with objects, you often need to write
functions and methods that accept objects as arguments. For example, the following code
shows a function named show_coin_status that accepts a Coin object as an argument:
def show_coin_status(coin_obj):
print('This side of the coin is up:', coin_obj.get_sideup())

The following code sample shows how we might create a Coin object, then pass it as an
argument to the show_coin_status function:
my_coin = coin.Coin()
show_coin_status(my_coin)

When you pass a object as an argument, the thing that is passed into the parameter variable
is a reference to the object. As a result, the function or method that receives the object as an
argument has access to the actual object. For example, look at the following flip method:
def flip(coin_obj):
coin_obj.toss()

This method accepts a Coin object as an argument, and it calls the object’s toss method.
Program 10-15 demonstrates the method.
10.3 Working with Instances 551

Program 10-15 (coin_argument.py)

1 # This program passes a Coin object as


2 # an argument to a function.
3 import coin
4
5 # main function
6 def main():
7 # Create a Coin object.
8 my_coin = coin.Coin()
9
10 # This will display 'Heads'.
11 print(my_coin.get_sideup())
12
13 # Pass the object to the flip function.
14 flip(my_coin)
15
16 # This might display 'Heads', or it might
17 # display 'Tails'.
18 print(my_coin.get_sideup())
19
20 # The flip function flips a coin.
21 def flip(coin_obj):
22 coin_obj.toss()
23
24 # Call the main function.
25 if _ _name_ _ == '_ _main_ _':
26 main()

Program Output
Heads
Tails

Program Output
Heads
Heads

Program Output
Heads
Tails

The statement in line 8 creates a Coin object, referenced by the variable my_coin.­
Line 11 displays the value of the my_coin object’s _ _sideup attribute. Because the object’s
_ _init_ _ method set the _ _sideup attribute to 'Heads', we know that line 11 will dis-
play the string 'Heads'. Line 14 calls the flip function, passing the my_coin object as
an argument. Inside the flip function, the my_coin object’s toss method is called. Then,
line 18 displays the value of the my_coin object’s _ _sideup attribute again. This time,
we cannot predict whether 'Heads' or 'Tails' will be displayed because the my_coin
object’s toss method has been called.
552 Chapter 10   Classes and Object-Oriented Programming

In the Spotlight:
Pickling Your Own Objects
Recall from Chapter 9 that the pickle module provides functions for serializing objects.
Serializing an object means converting it to a stream of bytes that can be saved to a file for
later retrieval. The pickle module’s dump function serializes (pickles) an object and writes it
to a file, and the load function retrieves an object from a file and deserializes (unpickles) it.
In Chapter 9, you saw examples in which dictionary objects were pickled and unpickled.
You can also pickle and unpickle objects of your own classes. Program 10-16 shows an
example that pickles three CellPhone objects and saves them to a file. Program 10-17
retrieves those objects from the file and unpickles them.

Program 10-16 (pickle_cellphone.py)

1 # This program pickles CellPhone objects.


2 import pickle
3 import cellphone
4
5 # Constant for the filename.
6 FILENAME = 'cellphones.dat'
7
8 def main():
9 # Initialize a variable to control the loop.
10 again = 'y'
11
12 # Open a file.
13 output_file = open(FILENAME, 'wb')
14
15 # Get data from the user.
16 while again.lower() == 'y':
17 # Get cell phone data.
18 man = input('Enter the manufacturer: ')
19 mod = input('Enter the model number: ')
20 retail = float(input('Enter the retail price: '))
21
22 # Create a CellPhone object.
23 phone = cellphone.CellPhone(man, mod, retail)
24
25 # Pickle the object and write it to the file.
26 pickle.dump(phone, output_file)
27
28 # Get more cell phone data?
29 again = input('Enter more phone data? (y/n): ')
30
31 # Close the file.
32 output_file.close()
33 print(f'The data was written to {FILENAME}.')
10.3 Working with Instances 553

34
35 # Call the main function.
36 if _ _name_ _ == '_ _main_ _':
37 main()

Program Output (with input shown in bold)


Enter the manufacturer: ACME Electronics Enter
Enter the model number: M1000 Enter
Enter the retail price: 199.99 Enter
Enter more phone data? (y/n): y Enter
Enter the manufacturer: Sonic Systems Enter
Enter the model number: X99 Enter
Enter the retail price: 299.99 Enter
Enter more phone data? (y/n): n Enter
The data was written to cellphones.dat.

Program 10-17 (unpickle_cellphone.py)

1 # This program unpickles CellPhone objects.


2 import pickle
3 import cellphone
4
5 # Constant for the filename.
6 FILENAME = 'cellphones.dat'
7
8 def main():
9 end_of_file = False # To indicate end of file
10
11 # Open the file.
12 input_file = open(FILENAME, 'rb')
13
14 # Read to the end of the file.
15 while not end_of_file:
16 try:
17 # Unpickle the next object.
18 phone = pickle.load(input_file)
19
20 # Display the cell phone data.
21 display_data(phone)
22 except EOFError:
23 # Set the flag to indicate the end
24 # of the file has been reached.
25 end_of_file = True
26
27 # Close the file.
28 input_file.close()
29
(program continues)
554 Chapter 10   Classes and Object-Oriented Programming

Program 10-17 (continued)

30    # The display_data function displays the data


31    # from the CellPhone object passed as an argument.
32 def display_data(phone):
33 print(f'Manufacturer: {phone.get_manufact()}')
34 print(f'Model Number: {phone.get_model()}')
35 print(f'Retail Price: ${phone.get_retail_price():,.2f}')
36 print()
37
38 # Call the main function.
39 if _ _name_ _ == '_ _main_ _':
40 main()

Program Output
Manufacturer: ACME Electronics
Model Number: M1000
Retail Price: $199.99

Manufacturer: Sonic Systems


Model Number: X99
Retail Price: $299.99

In the Spotlight:
Storing Objects in a Dictionary
Recall from Chapter 9 that dictionaries are objects that store elements as key-value pairs.
Each element in a dictionary has a key and a value. If you want to retrieve a specific value
from the dictionary, you do so by specifying its key. In Chapter 9, you saw examples that
stored values such as strings, integers, floating-point numbers, lists, and tuples in dictio-
naries. Dictionaries are also useful for storing objects that you create from your own classes.
Let’s look at an example. Suppose you want to create a program that keeps contact infor-
mation, such as names, phone numbers, and email addresses. You could start by writing a
class such as the Contact class, shown in Program 10-18. An instance of the Contact class
keeps the following data:
• A person’s name is stored in the _ _name attribute.
• A person’s phone number is stored in the _ _phone attribute.
• A person’s email address is stored in the _ _email attribute.
The class has the following methods:
• An _ _init_ _ method that accepts arguments for a person’s name, phone number,
and email address
• A set_name method that sets the _ _name attribute
10.3 Working with Instances 555

• A set_phone method that sets the _ _phone attribute


• A set_email method that sets the _ _email attribute
• A get_name method that returns the _ _name attribute
• A get_phone method that returns the _ _phone attribute
• A get_email method that returns the _ _email attribute
• A _ _str_ _ method that returns the object’s state as a string

Program 10-18 (contact.py)

1 # The Contact class holds contact information.


2
3 class Contact:
4 # The _ _init_ _ method initializes the attributes.
5 def _ _init_ _(self, name, phone, email):
6 self._ _name = name
7 self._ _phone = phone
8 self._ _email = email
9
10 # The set_name method sets the name attribute.
11 def set_name(self, name):
12 self._ _name = name
13
14 # The set_phone method sets the phone attribute.
15 def set_phone(self, phone):
16 self._ _phone = phone
17
18 # The set_email method sets the email attribute.
19 def set_email(self, email):
20 self._ _email = email
21
22 # The get_name method returns the name attribute.
23 def get_name(self):
24 return self._ _name
25
26 # The get_phone method returns the phone attribute.
27 def get_phone(self):
28 return self._ _phone
29
30 # The get_email method returns the email attribute.
31 def get_email(self):
32 return self._ _email
33
34 # The _ _str_ _ method returns the object's state
35 # as a string.
36 def _ _str_ _(self):
37    return f'Name: {self._ _name}\n' + \
38 f'Phone: {self._ _phone}\n' + \
39 f'Email: {self._ _email}'
556 Chapter 10   Classes and Object-Oriented Programming

Next, you could write a program that keeps Contact objects in a dictionary. Each time
the program creates a Contact object holding a specific person’s data, that object would
be stored as a value in the dictionary, using the person’s name as the key. Then, any time
you need to retrieve a specific person’s data, you would use that person’s name as a key to
retrieve the Contact object from the dictionary.
Program 10-19 shows an example. The program displays a menu that allows the user to
perform any of the following operations:
• Look up a contact in the dictionary
• Add a new contact to the dictionary
• Change an existing contact in the dictionary
• Delete a contact from the dictionary
• Quit the program
Additionally, the program automatically pickles the dictionary and saves it to a file when
the user quits the program. When the program starts, it automatically retrieves and unpick-
les the dictionary from the file. (Recall from Chapter 10 that pickling an object saves it to a
file, and unpickling an object retrieves it from a file.) If the file does not exist, the program
starts with an empty dictionary.
The program is divided into eight functions: main, load_contacts, get_menu_choice,
look_up, add, change, delete, and save_contacts. Rather than presenting the entire
program at once, let’s first examine the beginning part, which includes the import state-
ments, global constants, and the main function.

Program 10-19 (contact_manager.py: main function)

1 # This program manages contacts.


2 import contact
3 import pickle
4
5 # Global constants for menu choices
6 LOOK_UP = 1
7 ADD = 2
8 CHANGE = 3
9 DELETE = 4
10 QUIT = 5
11
12 # Global constant for the filename
13 FILENAME = 'contacts.dat'
14
15 # main function
16 def main():
17 # Load the existing contact dictionary and
18 # assign it to mycontacts.
19 mycontacts = load_contacts()
20
21 # Initialize a variable for the user's choice.
22 choice = 0
10.3 Working with Instances 557

23
24 # Process menu selections until the user
25 # wants to quit the program.
26 while choice != QUIT:
27 # Get the user's menu choice.
28 choice = get_menu_choice()
29
30 # Process the choice.
31 if choice == LOOK_UP:
32 look_up(mycontacts)
33 elif choice == ADD:
34 add(mycontacts)
35 elif choice == CHANGE:
36 change(mycontacts)
37 elif choice == DELETE:
38 delete(mycontacts)
39
40 # Save the mycontacts dictionary to a file.
41 save_contacts(mycontacts)
42

Line 2 imports the contact module, which contains the Contact class. Line 3 imports the
pickle module. The global constants that are initialized in lines 6 through 10 are used to
test the user’s menu selection. The FILENAME constant that is initialized in line 13 holds the
name of the file that will contain the pickled copy of the dictionary, which is contacts.dat.
Inside the main function, line 19 calls the load_contacts function. Keep in mind that if
the program has been run before and names were added to the dictionary, those names
have been saved to the contacts.dat file. The load_contacts function opens the file,
gets the dictionary from it, and returns a reference to the dictionary. If the program has not
been run before, the contacts.dat file does not exist. In that case, the load_contacts
function creates an empty dictionary and returns a reference to it. So, after the statement in
line 19 executes, the mycontacts variable references a dictionary. If the program has been
run before, mycontacts references a dictionary containing Contact objects. If this is the
first time the program has run, mycontacts references an empty dictionary.
Line 22 initializes the choice variable with the value 0. This variable will hold the user’s
menu selection.
The while loop that begins in line 26 repeats until the user chooses to quit the program.
Inside the loop, line 28 calls the get_menu_choice function. The get_menu_choice func-
tion displays the following menu:
1. Look up a contact
2. Add a new contact
3. Change an existing contact
4. Delete a contact
5. Quit the program
558 Chapter 10   Classes and Object-Oriented Programming

The user’s selection is returned from the get_menu_choice function and is assigned to the
choice variable.

The if-elif statement in lines 31 through 38 processes the user’s menu choice. If the user
selects item 1, line 32 calls the look_up function. If the user selects item 2, line 34 calls the
add function. If the user selects item 3, line 36 calls the change function. If the user selects
item 4, line 38 calls the delete function.
When the user selects item 5 from the menu, the while loop stops repeating, and the
statement in line 41 executes. This statement calls the save_contacts function, passing
mycontacts as an argument. The save_contacts function saves the mycontacts dictio­
nary to the contacts.dat file.
The load_contacts function is next.

Program 10-19 (contact_manager.py: load_contacts function)

43 def load_contacts():
44 try:
45 # Open the contacts.dat file.
46 input_file = open(FILENAME, 'rb')
47
48 # Unpickle the dictionary.
49 contact_dct = pickle.load(input_file)
50
51 # Close the phone_inventory.dat file.
52 input_file.close()
53 except IOError:
54 # Could not open the file, so create
55 # an empty dictionary.
56 contact_dct = {}
57
58 # Return the dictionary.
59 return contact_dct
60

Inside the try suite, line 46 attempts to open the contacts.dat file. If the file is successfully
opened, line 49 loads the dictionary object from it, unpickles it, and assigns it to the con-
tact_dct variable. Line 52 closes the file.

If the contacts.dat file does not exist (this will be the case the first time the program
runs), the statement in line 46 raises an IOError exception. That causes the program to
jump to the except clause in line 53. Then, the statement in line 56 creates an empty dic-
tionary and assigns it to the contact_dct variable.
The statement in line 59 returns the contact_dct variable.
The get_menu_choice function is next.
10.3 Working with Instances 559

Program 10-19 (contact_manager.py: get_menu_choice function)

61 # The get_menu_choice function displays the menu


62 # and gets a validated choice from the user.
63 def get_menu_choice():
64 print()
65 print('Menu')
66 print('---------------------------')
67 print('1. Look up a contact')
68 print('2. Add a new contact')
69 print('3. Change an existing contact')
70 print('4. Delete a contact')
71 print('5. Quit the program')
72 print()
73
74 # Get the user's choice.
75 choice = int(input('Enter your choice: '))
76
77 # Validate the choice.
78 while choice < LOOK_UP or choice > QUIT:
79 choice = int(input('Enter a valid choice: '))
80
81 # return the user's choice.
82 return choice
83

The statements in lines 64 through 72 display the menu on the screen. Line 75 prompts
the user to enter his or her choice. The input is converted to an int and assigned to the
choice variable. The while loop in lines 78 through 79 validates the user’s input and, if
necessary, prompts the user to reenter his or her choice. Once a valid choice is entered, it
is returned from the function in line 82.
The look_up function is next.

Program 10-19 (contact_manager.py: look_up function)

84 # The look_up function looks up an item in the


85 # specified dictionary.
86 def look_up(mycontacts):
87 # Get a name to look up.
88 name = input('Enter a name: ')
89
90 # Look it up in the dictionary.
91 print(mycontacts.get(name, 'That name is not found.'))
92
560 Chapter 10   Classes and Object-Oriented Programming

The purpose of the look_up function is to allow the user to look up a specified contact.
It accepts the mycontacts dictionary as an argument. Line 88 prompts the user to enter a
name, and line 91 passes that name as an argument to the dictionary’s get function. One of
the following actions will happen as a result of line 91:
• If the specified name is found as a key in the dictionary, the get method returns a
reference to the Contact object that is associated with that name. The Contact object
is then passed as an argument to the print function. The print function displays the
string that is returned from the Contact object’s _ _str_ _ method.
• If the specified name is not found as a key in the dictionary, the get method returns
the string 'That name is not found.', which is displayed by the print function.
The add function is next.

Program 10-19 (contact_manager.py: add function)

93 # The add function adds a new entry into the


94 # specified dictionary.
95 def add(mycontacts):
96 # Get the contact info.
97 name = input('Name: ')
98 phone = input('Phone: ')
99 email = input('Email: ')
100
101 # Create a Contact object named entry.
102 entry = contact.Contact(name, phone, email)
103
104 # If the name does not exist in the dictionary,
105 # add it as a key with the entry object as the
106 # associated value.
107 if name not in mycontacts:
108 mycontacts[name] = entry
109 print('The entry has been added.')
110 else:
111 print('That name already exists.')
112

The purpose of the add function is to allow the user to add a new contact to the dictionary.
It accepts the mycontacts dictionary as an argument. Lines 97 through 99 prompt the user
to enter a name, a phone number, and an email address. Line 102 creates a new Contact
object, initialized with the data entered by the user.
The if statement in line 107 determines whether the name is already in the dictionary. If
not, line 108 adds the newly created Contact object to the dictionary, and line 109 prints
a message indicating that the new data is added. Otherwise, a message indicating that the
entry already exists is printed in line 111.
The change function is next.
10.3 Working with Instances 561

Program 10-19 (contact_manager.py: change function)

113 # The change function changes an existing


114 # entry in the specified dictionary.
115 def change(mycontacts):
116 # Get a name to look up.
117 name = input('Enter a name: ')
118
119 if name in mycontacts:
120 # Get a new phone number.
121 phone = input('Enter the new phone number: ')
122
123 # Get a new email address.
124 email = input('Enter the new email address: ')
125
126 # Create a contact object named entry.
127 entry = contact.Contact(name, phone, email)
128
129 # Update the entry.
130 mycontacts[name] = entry
131 print('Information updated.')
132 else:
133 print('That name is not found.')
134

The purpose of the change function is to allow the user to change an existing contact in
the dictionary. It accepts the mycontacts dictionary as an argument. Line 117 gets a name
from the user. The if statement in line 119 determines whether the name is in the dictio-
nary. If so, line 121 gets the new phone number, and line 124 gets the new email address.
Line 127 creates a new Contact object initialized with the existing name and the new
phone number and email address. Line 130 stores the new Contact object in the dictio-
nary, using the existing name as the key.
If the specified name is not in the dictionary, line 133 prints a message indicating so.
The delete function is next.

Program 10-19 (contact_manager.py: delete function)

135 # The delete function deletes an entry from the


136 # specified dictionary.
137 def delete(mycontacts):
138 # Get a name to look up.
139 name = input('Enter a name: ')
140
141 # If the name is found, delete the entry.
142 if name in mycontacts:
143 del mycontacts[name]
(program continues)
562 Chapter 10   Classes and Object-Oriented Programming

Program 10-19 (continued)

144 print('Entry deleted.')


145 else:
146 print('That name is not found.')
147

The purpose of the delete function is to allow the user to delete an existing contact from
the dictionary. It accepts the mycontacts dictionary as an argument. Line 139 gets a name
from the user. The if statement in line 142 determines whether the name is in the dictio-
nary. If so, line 143 deletes it, and line 144 prints a message indicating that the entry was
deleted. If the name is not in the dictionary, line 146 prints a message indicating so.
The save_contacts function is next.

Program 10-19 (contact_manager.py: save_contacts function)

148 # The save_contacts funtion pickles the specified


149 # object and saves it to the contacts file.
150 def save_contacts(mycontacts):
151 # Open the file for writing.
152 output_file = open(FILENAME, 'wb')
153
154 # Pickle the dictionary and save it.
155 pickle.dump(mycontacts, output_file)
156
157 # Close the file.
158 output_file.close()
159
160 # Call the main function.
161 if _ _name_ _ == '_ _main_ _':
162 main()

The save_contacts function is called just before the program stops running. It accepts the
mycontacts dictionary as an argument. Line 152 opens the contacts.dat file for writing.
Line 155 pickles the mycontacts dictionary and saves it to the file. Line 158 closes the file.
The following program output shows two sessions with the program. The sample output
does not demonstrate everything the program can do, but it does demonstrate how contacts
are saved when the program ends and then loaded when the program runs again.

Program Output (with input shown in bold)


Menu
---------------------------
1. Look up a contact
2. Add a new contact
10.3 Working with Instances 563

3. Change an existing contact


4. Delete a contact
5. Quit the program

Enter your choice: 2 Enter


Name: Matt Goldstein Enter
Phone: 617-555-1234 Enter
Email: [email protected] Enter
The entry has been added.

Menu
---------------------------
1. Look up a contact
2. Add a new contact
3. Change an existing contact
4. Delete a contact
5. Quit the program

Enter your choice: 2 Enter


Name: Jorge Ruiz Enter
Phone: 919-555-1212 Enter
Email: [email protected] Enter
The entry has been added.

Menu
---------------------------
1. Look up a contact
2. Add a new contact
3. Change an existing contact
4. Delete a contact
5. Quit the program

Enter your choice: 5 Enter

Program Output (with input shown in bold)


Menu
---------------------------
1. Look up a contact
2. Add a new contact
3. Change an existing contact
4. Delete a contact
5. Quit the program

Enter your choice: 1 Enter


Enter a name: Matt Goldstein Enter
Name: Matt Goldstein
Phone: 617-555-1234
Email: [email protected]
(program output continues)
564 Chapter 10   Classes and Object-Oriented Programming

Program Output (continued)


Menu
---------------------------
1. Look up a contact
2. Add a new contact
3. Change an existing contact
4. Delete a contact
5. Quit the program

Enter your choice: 1 Enter


Enter a name: Jorge Ruiz Enter
Name: Jorge Ruiz
Phone: 919-555-1212
Email: [email protected]

Menu
---------------------------
1. Look up a contact
2. Add a new contact
3. Change an existing contact
4. Delete a contact
5. Quit the program

Enter your choice: 5 Enter

Checkpoint
10.12 What is an instance attribute?
10.13 A program creates 10 instances of the Coin class. How many _ _sideup
attributes exist in memory?
10.14 What is an accessor method? What is a mutator method?

10.4 Techniques for Designing Classes

The Unified Modeling Language


When designing a class, it is often helpful to draw a UML diagram. UML stands for
Unified Modeling Language. It provides a set of standard diagrams for graphically depict-
ing object-oriented systems. Figure 10-9 shows the general layout of a UML diagram for
a class. Notice the diagram is a box that is divided into three sections. The top section is
where you write the name of the class. The middle section holds a list of the class’s data
attributes. The bottom section holds a list of the class’s methods.
10.4 Techniques for Designing Classes 565

Figure 10-9 General layout of a UML diagram for a class

Class name goes here

Data attributes are listed here

Methods are listed here

Following this layout, Figure 10-10 and 10-11 show UML diagrams for the Coin class and
the CellPhone class that you saw previously in this chapter. Notice we did not show the
self parameter in any of the methods, since it is understood that the self parameter is
required.

Figure 10-10 UML diagram for the Coin class

Coin

__sideup

__init__( )
toss( )
get_sideup( )

Figure 10-11 UML diagram for the CellPhone class

CellPhone

__manufact
__model
__retail_price
__init__(manufact, model, price)
set_manufact(manufact)
set_model(model)
set_retail_price(price)
get_manufact()
get_model()
get_retail_price()

Finding the Classes in a Problem


When developing an object-oriented program, one of your first tasks is to identify the
classes that you will need to create. Typically, your goal is to identify the different types
of real-world objects that are present in the problem, then create classes for those types of
objects within your application.
Over the years, software professionals have developed numerous techniques for finding the
classes in a given problem. One simple and popular technique involves the following steps:
1. Get a written description of the problem domain.
2. Identify all the nouns (including pronouns and noun phrases) in the description. Each
of these is a potential class.
3. Refine the list to include only the classes that are relevant to the problem.
Let’s take a closer look at each of these steps.
566 Chapter 10   Classes and Object-Oriented Programming

Writing a Description of the Problem Domain


The problem domain is the set of real-world objects, parties, and major events related to the
problem. If you adequately understand the nature of the problem you are trying to solve,
you can write a description of the problem domain yourself. If you do not thoroughly under-
stand the nature of the problem, you should have an expert write the description for you.
For example, suppose we are writing a program that the manager of Joe’s Automotive Shop
will use to print service quotes for customers. Here is a description that an expert, perhaps
Joe himself, might have written:
Joe’s Automotive Shop services foreign cars and specializes in servicing cars made by Mercedes,
Porsche, and BMW. When a customer brings a car to the shop, the manager gets the customer’s
name, address, and telephone number. The manager then determines the make, model, and year
of the car and gives the customer a service quote. The service quote shows the estimated parts
charges, estimated labor charges, sales tax, and total estimated charges.

The problem domain description should include any of the following:


• Physical objects such as vehicles, machines, or products
• Any role played by a person, such as manager, employee, customer, teacher, student,
etc.
• The results of a business event, such as a customer order, or in this case a service quote
• Recordkeeping items, such as customer histories and payroll records

Identify All of the Nouns


The next step is to identify all of the nouns and noun phrases. (If the description contains pro-
nouns, include them too.) Here’s another look at the previous problem domain description.
This time the nouns and noun phrases appear in bold.
Joe’s Automotive Shop services foreign cars and specializes in servicing cars made by Mercedes,
Porsche, and BMW. When a customer brings a car to the shop, the manager gets the customer’s
name, address, and telephone number. The manager then determines the make, model, and year
of the car and gives the customer a service quote. The service quote shows the estimated parts
charges, estimated labor charges, sales tax, and total estimated charges.

Notice some of the nouns are repeated. The following list shows all of the nouns without
duplicating any of them:
address
BMW
car
cars
customer
estimated labor charges
estimated parts charges
foreign cars
Joe’s Automotive Shop
make
manager
Mercedes
model
10.4 Techniques for Designing Classes 567

name
Porsche
sales tax
service quote
shop
telephone number
total estimated charges
year

Refining the List of Nouns


The nouns that appear in the problem description are merely candidates to become classes.
It might not be necessary to make classes for them all. The next step is to refine the list to
include only the classes that are necessary to solve the particular problem at hand. We will
look at the common reasons that a noun can be eliminated from the list of potential classes.
1. Some of the nouns really mean the same thing.
In this example, the following sets of nouns refer to the same thing:
• car, cars, and foreign cars
These all refer to the general concept of a car.
• Joe’s Automotive Shop and shop
Both of these refer to the company “Joe’s Automotive Shop.”
We can settle on a single class for each of these. In this example, we will arbitrarily eliminate
cars and foreign cars from the list and use the word car. Likewise, we will eliminate Joe’s
Automotive Shop from the list and use the word shop. The updated list of potential classes is:

address
BMW
car
cars
customer Because car, cars, and foreign cars mean
estimated labor charges the same thing in this problem, we have
eliminated cars and foreign cars. Also,
estimated parts charges
because Joe’s Automotive Shop and shop
foreign cars mean the same thing, we have eliminated
Joe’s Automotive Shop Joe’s Automotive Shop.
make
manager
Mercedes
model
name
Porsche
sales tax
service quote
(continued)
568 Chapter 10   Classes and Object-Oriented Programming

shop
telephone number
total estimated charges
year

2. Some nouns might represent items that we do not need to be concerned with in order
to solve the problem.
A quick review of the problem description reminds us of what our application should do:
print a service quote. In this example, we can eliminate two unnecessary classes from the list:
• We can cross shop off the list because our application only needs to be concerned with
individual service quotes. It doesn’t need to work with or determine any company-wide
information. If the problem description asked us to keep a total of all the service quotes,
then it would make sense to have a class for the shop.
• We will not need a class for the manager because the problem statement does not
direct us to process any information about the manager. If there were multiple shop
managers, and the problem description had asked us to record which manager gener-
ated each service quote, then it would make sense to have a class for the manager.
The updated list of potential classes at this point is:

address
BMW
car
cars
customer
estimated labor charges
estimated parts charges
foreign cars
Joe’s Automotive Shop
make
Our problem description does not direct us to
manager process any information about the shop, or any
Mercedes information about the manager, so we have
model eliminated those from the list.
name
Porsche
sales tax
service quote
shop
telephone number
total estimated charges
year
10.4 Techniques for Designing Classes 569

3. Some of the nouns might represent objects, not classes.


We can eliminate Mercedes, Porsche, and BMW as classes because, in this example, they
all represent specific cars and can be considered instances of a car class. At this point, the
updated list of potential classes is:

address
BMW
car
cars
customer
estimated labor charges
estimated parts charges
foreign cars We have eliminated Mercedes, Porsche, and
Joe’s Automotive Shop BMW because they are all instances of a car
class. That means that these nouns identify
manager
objects, not classes.
make
Mercedes
model
name
Porsche
sales tax
service quote
shop
telephone number
total estimated charges
year

NOTE: Some object-oriented designers take note of whether a noun is plural or singular.
Sometimes a plural noun will indicate a class, and a singular noun will indicate an object.

4. Some of the nouns might represent simple values that can be assigned to a variable
and do not require a class.
Remember, a class contains data attributes and methods. Data attributes are related items
that are stored in an object of the class and define the object’s state. Methods are actions
or behaviors that can be performed by an object of the class. If a noun represents a type
of item that would not have any identifiable data attributes or methods, then it can prob-
ably be eliminated from the list. To help determine whether a noun represents an item that
would have data attributes and methods, ask the following questions about it:
• Would you use a group of related values to represent the item’s state?
• Are there any obvious actions to be performed by the item?
570 Chapter 10   Classes and Object-Oriented Programming

If the answers to both of these questions are no, then the noun probably represents a value
that can be stored in a simple variable. If we apply this test to each of the nouns that remain
in our list, we can conclude that the following are probably not classes: address, estimated
labor charges, estimated parts charges, make, model, name, sales tax, telephone number,
total estimated charges, and year. These are all simple string or numeric values that can be
stored in variables. Here is the updated list of potential classes:

Address
BMW
car
cars
customer
estimated labor charges We have eliminated address, estimated
estimated parts charges labor charges, estimated parts charges,
make, model, name, sales tax, telephone
foreign cars
number, total estimated charges, and
Joe’s Automotive Shop year as classes because they represent
make simple values that can be stored in
manager variables.
Mercedes
model
name
Porsche
sales tax
service quote
shop
telephone number
total estimated charges
year

As you can see from the list, we have eliminated everything except car, customer, and
service quote. This means that in our application, we will need classes to represent cars,
customers, and service quotes. Ultimately, we will write a Car class, a Customer class, and
a ServiceQuote class.

Identifying a Class’s Responsibilities


Once the classes have been identified, the next task is to identify each class’s responsibilities.
A class’s responsibilities are:
• the things that the class is responsible for knowing.
• the actions that the class is responsible for doing.
10.4 Techniques for Designing Classes 571

When you have identified the things that a class is responsible for knowing, then you have
identified the class’s data attributes. Likewise, when you have identified the actions that a
class is responsible for doing, you have identified its methods.
It is often helpful to ask the questions “In the context of this problem, what must the class
know? What must the class do?” The first place to look for the answers is in the descrip-
tion of the problem domain. Many of the things that a class must know and do will be
mentioned. Some class responsibilities, however, might not be directly mentioned in the
problem domain, so further consideration is often required. Let’s apply this methodology
to the classes we previously identified from our problem domain.

The Customer Class


In the context of our problem domain, what must the Customer class know? The descrip-
tion directly mentions the following items, which are all data attributes of a customer:
• the customer’s name
• the customer’s address
• the customer’s telephone number
These are all values that can be represented as strings and stored as data attributes. The
Customer class can potentially know many other things. One mistake that can be made at
this point is to identify too many things that an object is responsible for knowing. In some
applications, a Customer class might know the customer’s email address. This particular
problem domain does not mention that the customer’s email address is used for any pur-
pose, so we should not include it as a responsibility.
Now, let’s identify the class’s methods. In the context of our problem domain, what must
the Customer class do? The only obvious actions are:
• initialize an object of the Customer class.
• set and return the customer’s name.
• set and return the customer’s address.
• set and return the customer’s telephone number.
From this list we can see that the Customer class will have an _ _init_ _ method, as well
as accessors and mutators for the data attributes. Figure 10-12 shows a UML diagram for
the Customer class. The Python code for the class is shown in Program 10-20.

Figure 10-12 UML diagram for the Customer class

Customer
__name
__address
__phone
__init__(name, address,
phone)
set_name(name)
set_address(address)
set_phone(phone)
get_name()
get_address()
get_phone()
572 Chapter 10   Classes and Object-Oriented Programming

Program 10-20 (customer.py)

1 # Customer class
2 class Customer:
3 def _ _init_ _(self, name, address, phone):
4 self._ _name = name
5 self._ _address = address
6 self._ _phone = phone
7
8 def set_name(self, name):
9 self._ _name = name
10
11 def set_address(self, address):
12 self._ _address = address
13
14 def set_phone(self, phone):
15 self._ _phone = phone
16
17 def get_name(self):
18 return self._ _name
19
20 def get_address(self):
21 return self._ _address
22
23 def get_phone(self):
24 return self._ _phone

The Car Class


In the context of our problem domain, what must an object of the Car class know? The
following items are all data attributes of a car and are mentioned in the problem domain:
• the car’s make
• the car’s model
• the car’s year
Now let’s identify the class’s methods. In the context of our problem domain, what must
the Car class do? Once again, the only obvious actions are the standard set of methods that
we will find in most classes (an _ _init_ _ method, accessors, and mutators). Specifically,
the actions are:
• initialize an object of the Car class.
• set and get the car’s make.
• set and get the car’s model.
• set and get the car’s year.
Figure 10-13 shows a UML diagram for the Car class at this point. The Python code for
the class is shown in Program 10-21.
10.4 Techniques for Designing Classes 573

Figure 10-13 UML diagram for the Car class

Car

__make
__model
__year
__init__(make, model,
year)
set_make(make)
set_model(make)
set_year(y)
get_make( )
get_model( )
get_year( )

Program 10-21 (car.py)

1 # Car class
2 class Car:
3 def _ _init_ _(self, make, model, year):
4 self._ _make = make
5 self._ _model = model
6 self._ _year = year
7
8 def set_make(self, make):
9 self._ _make = make
10
11 def set_model(self, model):
12 self._ _model = model
13
14 def set_year(self, year):
15 self._ _year = year
16
17 def get_make(self):
18 return self._ _make
19
20 def get_model(self):
21 return self._ _model
22
23 def get_year(self):
24 return self._ _year

The ServiceQuote Class


In the context of our problem domain, what must an object of the ServiceQuote class
know? The problem domain mentions the following items:
• the estimated parts charges
• the estimated labor charges
574 Chapter 10   Classes and Object-Oriented Programming

• the sales tax


• the total estimated charges
The methods that we will need for this class are an _ _init_ _ method and the accessors
and mutators for the estimated parts charges and estimated labor charges attributes. In
addition, the class will need methods that calculate and return the sales tax and the total
estimated charges. Figure 10-14 shows a UML diagram for the ServiceQuote class.
Program 10-22 shows an example of the class in Python code.

Figure 10-14 UML diagram for the ServiceQuote class

ServiceQuote

__parts_charges
__labor_charges
__init__(pcharge, lcharge)
set_parts_charges(pcharge)
set_labor_charges(lcharge)
get_parts_charges( )
get_labor_charges( )
get_sales_tax( )
get_total_charges( )

Program 10-22 (servicequote.py)

1 # Constant for the sales tax rate


2 TAX_RATE = 0.05
3
4 # ServiceQuote class
5 class ServiceQuote:
6 def _ _init_ _(self, pcharge, lcharge):
7 self._ _parts_charges = pcharge
8 self._ _labor_charges = lcharge
9
10 def set_parts_charges(self, pcharge):
11 self._ _parts_charges = pcharge
12
13 def set_labor_charges(self, lcharge):
14 self._ _labor_charges = lcharge
15
16 def get_parts_charges(self):
17 return self._ _parts_charges
18
19 def get_labor_charges(self):
20 return self._ _labor_charges
21
22 def get_sales_tax(self):
23 return _ _parts_charges * TAX_RATE
24
Review Questions 575

25 def get_total_charges(self):
26 return _ _parts_charges + _ _labor_charges + \
27 (_ _parts_charges * TAX_RATE)

This Is only the Beginning


You should look at the process that we have discussed in this section merely as a starting
point. It’s important to realize that designing an object-oriented application is an iterative
process. It may take you several attempts to identify all of the classes that you will need and
determine all of their responsibilities. As the design process unfolds, you will gain a deeper
understanding of the problem, and consequently you will see ways to improve the design.

Checkpoint
10.15 The typical UML diagram for a class has three sections. What appears in these
three sections?
10.16 What is a problem domain?
10.17 When designing an object-oriented application, who should write a description of
the problem domain?
10.18 How do you identify the potential classes in a problem domain description?
10.19 What are a class’s responsibilities?
10.20 What two questions should you ask to determine a class’s responsibilities?
10.21 Will all of a class’s actions always be directly mentioned in the problem domain
description?

Review Questions
Multiple Choice
1. The ______________ programming practice is centered on creating functions that are
separate from the data that they work on.
a. modular
b. procedural
c. functional
d. object-oriented
2. The ______________ programming practice is centered on creating objects.
a. object-centric
b. objective
c. procedural
d. object-oriented
576 Chapter 10   Classes and Object-Oriented Programming

3. A(n) ______________ is a component of a class that references data.


a. method
b. instance
c. data attribute
d. module
4. An object is a(n) ______________.
a. blueprint
b. cookie cutter
c. variable
d. instance
5. By doing this, you can hide a class’s attribute from code outside the class.
a. avoid using the self parameter to create the attribute
b. begin the attribute’s name with two underscores
c. begin the name of the attribute with private_ _
d. begin the name of the attribute with the @ symbol
6. A(n) ______________ method gets the value of a data attribute but does not change it.
a. retriever
b. constructor
c. mutator
d. accessor
7. A(n) ______________ method stores a value in a data attribute or changes its value in
some other way.
a. modifier
b. constructor
c. mutator
d. accessor
8. The ______________ method is automatically called when an object is created.
a. _ _init_ _
b. init
c. _ _str_ _
d. _ _object_ _
9. If a class has a method named _ _str_ _, which of these is a way to call the method?
a. you call it like any other method: object._ _str_ _()
b. by passing an instance of the class to the built in str function
c. the method is automatically called when the object is created
d. by passing an instance of the class to the built-in state function
10. A set of standard diagrams for graphically depicting object-oriented systems is
­provided by ______________.
a. the Unified Modeling Language
b. flowcharts
c. pseudocode
d. the Object Hierarchy System
Review Questions 577

11. In one approach to identifying the classes in a problem, the programmer identifies the
______________ in a description of the problem domain.
a. verbs
b. adjectives
c. adverbs
d. nouns
12. In one approach to identifying a class’s data attributes and methods, the programmer
identifies the class’s ______________.
a. responsibilities
b. name
c. synonyms
d. nouns

True or False
1. The practice of procedural programming is centered on the creation of objects.
2. Object reusability has been a factor in the increased use of object-oriented programming.
3. It is a common practice in object-oriented programming to make all of a class’s data
attributes accessible to statements outside the class.
4. A class method does not have to have a self parameter.
5. Starting an attribute name with two underscores will hide the attribute from code
outside the class.
6. You cannot directly call the _ _str_ _ method.
7. One way to find the classes needed for an object-oriented program is to identify all of
the verbs in a description of the problem domain.

Short Answer
1. What is encapsulation?
2. Why should an object’s data attributes be hidden from code outside the class?
3. What is the difference between a class and an instance of a class?
4. The following statement calls an object’s method. What is the name of the method?
What is the name of the variable that references the object?
wallet.get_dollar()
5. When the _ _init_ _ method executes, what does the self parameter reference?
6. In a Python class, how do you hide an attribute from code outside the class?
7. How do you call the _ _str_ _ method?

Algorithm Workbench
1. Suppose my_car is the name of a variable that references an object, and go is the name
of a method. Write a statement that uses the my_car variable to call the go method.
(You do not have to pass any arguments to the go method.)
578 Chapter 10   Classes and Object-Oriented Programming

2. Write a class definition named Book. The Book class should have data attributes for a
book’s title, the author’s name, and the publisher’s name. The class should also have
the following:
a. An _ _init_ _ method for the class. The method should accept an argument for
each of the data attributes.
b. Accessor and mutator methods for each data attribute.
c. An _ _str_ _ method that returns a string indicating the state of the object.
3. Look at the following description of a problem domain:
The bank offers the following types of accounts to its customers: savings accounts,
checking accounts, and money market accounts. Customers are allowed to deposit
money into an account (thereby increasing its balance), withdraw money from an
account (thereby decreasing its balance), and earn interest on the account. Each
account has an interest rate.
Assume that you are writing a program that will calculate the amount of interest
earned for a bank account.
a. Identify the potential classes in this problem domain.
b. Refine the list to include only the necessary class or classes for this problem.
c. Identify the responsibilities of the class or classes.

Programming Exercises
1. Pet Class
VideoNote
Write a class named Pet, which should have the following data attributes:
The Pet class
• _ _name (for the name of a pet)
• _ _animal_type (for the type of animal that a pet is. Example values are ‘Dog’, ‘Cat’,
and ‘Bird’)
• _ _age (for the pet’s age)
The Pet class should have an _ _init_ _ method that creates these attributes. It should also
have the following methods:
• set_name
This method assigns a value to the _ _name field.
• set_animal_type
This method assigns a value to the _ _animal_type field.
• set_age
This method assigns a value to the _ _age field.
• get_name
This method returns the value of the _ _ name field.
• get_animal_type
This method returns the value of the _ _animal_type field.
• get_age
This method returns the value of the _ _age field.
Once you have written the class, write a program that creates an object of the class and
prompts the user to enter the name, type, and age of his or her pet. This data should be
Programming Exercises 579

stored as the object’s attributes. Use the object’s accessor methods to retrieve the pet’s
name, type, and age and display this data on the screen.
2. Car Class
Write a class named Car that has the following data attributes:
• _ _year_model (for the car’s year model)
• _ _make (for the make of the car)
• _ _speed (for the car’s current speed)
The Car class should have an _ _init_ _ method that accepts the car’s year model and
make as arguments. These values should be assigned to the object’s _ _year_model and
_ _make data attributes. It should also assign 0 to the _ _speed data attribute.
The class should also have the following methods:
• accelerate
The accelerate method should add 5 to the speed data attribute each time it is called.
• brake
The brake method should subtract 5 from the speed data attribute each time it is called.
• get_speed
The get_speed method should return the current speed.
Next, design a program that creates a Car object then calls the accelerate method five
times. After each call to the accelerate method, get the current speed of the car and dis-
play it. Then call the brake method five times. After each call to the brake method, get the
current speed of the car and display it.
3. Personal Information Class
Design a class that holds the following personal data: name, address, age, and phone num-
ber. Write appropriate accessor and mutator methods. Also, write a program that creates
three instances of the class. One instance should hold your information, and the other two
should hold your friends’ or family members’ information.
4. Employee Class
Write a class named Employee that holds the following data about an employee in attrib-
utes: name, ID number, department, and job title.
Once you have written the class, write a program that creates three Employee objects to
hold the following data:

Name ID Number Department Job Title


Susan Meyers 47899 Accounting Vice President
Mark Jones 39119 IT Programmer
Joy Rogers 81774 Manufacturing Engineer

The program should store this data in the three objects, then display the data for each
employee on the screen.
580 Chapter 10   Classes and Object-Oriented Programming

5. RetailItem Class
Write a class named RetailItem that holds data about an item in a retail store. The class
should store the following data in attributes: item description, units in inventory, and price.
Once you have written the class, write a program that creates three RetailItem objects
and stores the following data in them:

Description Units in Inventory Price


Item #1 Jacket 12 59.95
Item #2 Designer Jeans 40 34.95
Item #3 Shirt 20 24.95

6. Patient Charges
Write a class named Patient that has attributes for the following data:
• First name, middle name, and last name
• Address, city, state, and ZIP code
• Phone number
• Name and phone number of emergency contact
The Patient class’s _ _init_ _ method should accept an argument for each attribute. The
Patient class should also have accessor and mutator methods for each attribute.
Next, write a class named Procedure that represents a medical procedure that has been
performed on a patient. The Procedure class should have attributes for the following data:
• Name of the procedure
• Date of the procedure
• Name of the practitioner who performed the procedure
• Charges for the procedure
The Procedure class’s _ _init_ _ method should accept an argument for each attribute.
The Procedure class should also have accessor and mutator methods for each attribute.
Next, write a program that creates an instance of the Patient class, initialized with sample
data. Then, create three instances of the Procedure class, initialized with the following data:

Procedure #1: Procedure #2: Procedure #3:


Procedure name: Physical Exam Procedure name: X-ray Procedure name: Blood test
Date: Today’s date Date: Today’s date Date: Today’s date
Practitioner: Dr. Irvine Practitioner: Dr. Jamison Practitioner: Dr. Smith
Charge: 250.00 Charge: 500.00 Charge: 200.00

The program should display the patient’s information, information about all three of the
procedures, and the total charges of the three procedures.
Programming Exercises 581

7. Employee Management System


This exercise assumes you have created the Employee class for Programming Exercise 4.
Create a program that stores Employee objects in a dictionary. Use the ­employee ID num-
ber as the key. The program should present a menu that lets the user perform the following
actions:
• Look up an employee in the dictionary
• Add a new employee to the dictionary
• Change an existing employee’s name, department, and job title in the dictionary
• Delete an employee from the dictionary
• Quit the program
When the program ends, it should pickle the dictionary and save it to a file. Each time the
program starts, it should try to load the pickled dictionary from the file. If the file does not
exist, the program should start with an empty dictionary.
8. Cash Register
This exercise assumes you have created the RetailItem class for Programming Exer­
cise 5. Create a CashRegister class that can be used with the RetailItem class. The
CashRegister class should be able to internally keep a list of RetailItem objects. The
class should have the following methods:
• A method named purchase_item that accepts a RetailItem object as an argument.
Each time the purchase_item method is called, the RetailItem object that is passed
as an argument should be added to the list.
• A method named get_total that returns the total price of all the RetailItem objects
stored in the CashRegister object’s internal list.
• A method named show_items that displays data about the RetailItem objects stored
in the CashRegister object’s internal list.
• A method named clear that should clear the CashRegister object’s internal list.
Demonstrate the CashRegister class in a program that allows the user to select several
items for purchase. When the user is ready to check out, the program should display a list
of all the items he or she has selected for purchase, as well as the total price.
9. Trivia Game
In this programming exercise, you will create a simple trivia game for two players. The pro-
gram will work like this:
• Starting with player 1, each player gets a turn at answering 5 trivia questions. (There
should be a total of 10 questions.) When a question is displayed, 4 possible answers are
also displayed. Only one of the answers is correct, and if the player selects the correct
answer, he or she earns a point.
• After answers have been selected for all the questions, the program displays the number
of points earned by each player and declares the player with the highest number of points
the winner.
582 Chapter 10   Classes and Object-Oriented Programming

To create this program, write a Question class to hold the data for a trivia question. The
Question class should have attributes for the following data:
• A trivia question
• Possible answer 1
• Possible answer 2
• Possible answer 3
• Possible answer 4
• The number of the correct answer (1, 2, 3, or 4)
The Question class also should have an appropriate _ _init_ _ method, accessors, and
mutators.
The program should have a list or a dictionary containing 10 Question objects, one for
each trivia question. Make up your own trivia questions on the subject or subjects of your
choice for the objects.
CHAPTER

11 Inheritance

TOPICS
11.1 Introduction to Inheritance
11.2 Polymorphism

11.1 Introduction to Inheritance

CONCEPT: Inheritance allows a new class to extend an existing class. The new class
inherits the members of the class it extends.

Generalization and Specialization


In the real world, you can find many objects that are specialized versions of other more
general objects. For example, the term “insect” describes a general type of creature with
various characteristics. Because grasshoppers and bumblebees are insects, they have all
the general characteristics of an insect. In addition, they have special characteristics of
their own. For example, the grasshopper has its jumping ability, and the bumblebee has
its stinger. Grasshoppers and bumblebees are specialized versions of an insect. This is
­illustrated in Figure 11-1.

583
584 Chapter 11  Inheritance

Figure 11-1 Bumblebees and grasshoppers are specialized versions of an insect

Inheritance and the “Is a” Relationship


When one object is a specialized version of another object, there is an “is a” relationship
between them. For example, a grasshopper is an insect. Here are a few other examples of
the “is a” relationship:
• A poodle is a dog.
• A car is a vehicle.
• A flower is a plant.
• A rectangle is a shape.
• A football player is an athlete.
When an “is a” relationship exists between objects, it means that the specialized object has
all of the characteristics of the general object, plus additional characteristics that make it
special. In object-oriented programming, inheritance is used to create an “is a” relationship
among classes. This allows you to extend the capabilities of a class by creating another class
that is a specialized version of it.
Inheritance involves a superclass and a subclass. The superclass is the general class and the
subclass is the specialized class. You can think of the subclass as an extended version of the
superclass. The subclass inherits attributes and methods from the superclass without any
of them having to be rewritten. Furthermore, new attributes and methods may be added to
the subclass, and that is what makes it a specialized version of the superclass.

NOTE: Superclasses are also called base classes, and subclasses are also called derived
classes. Either set of terms is correct. For consistency, this text will use the terms super-
class and subclass.

Let’s look at an example of how inheritance can be used. Suppose we are developing a
program that a car dealership can use to manage its inventory of used cars. The dealer-
ship’s inventory includes three types of automobiles: cars, pickup trucks, and sport-utility
11.1 Introduction to Inheritance 585

vehicles (SUVs). Regardless of the type, the dealership keeps the following data about each
automobile:
• Make
• Year model
• Mileage
• Price
Each type of vehicle that is kept in inventory has these general characteristics, plus its own
specialized characteristics. For cars, the dealership keeps the following additional data:
• Number of doors (2 or 4)
For pickup trucks, the dealership keeps the following additional data:
• Drive type (two-wheel drive or four-wheel drive)
And for SUVs, the dealership keeps the following additional data:
• Passenger capacity
In designing this program, one approach would be to write the following three classes:
• A Car class with data attributes for the make, year model, mileage, price, and the
number of doors.
• A Truck class with data attributes for the make, year model, mileage, price, and the
drive type.
• An SUV class with data attributes for the make, year model, mileage, price, and the
passenger capacity.
This would be an inefficient approach, however, because all three of the classes have a large
number of common data attributes. As a result, the classes would contain a lot of dupli-
cated code. In addition, if we discover later that we need to add more common attributes,
we would have to modify all three classes.
A better approach would be to write an Automobile superclass to hold all the general data
about an automobile, then write subclasses for each specific type of automobile. Program 11-1
shows the Automobile class’s code, which appears in a module named vehicles.

Program 11-1 (Lines 1 through 44 of vehicles.py)

1 # The Automobile class holds general data


2 # about an automobile in inventory.
3
4 class Automobile:
5 # The _ _init_ _method accepts arguments for the
6 # make, model, mileage, and price. It initializes
7 # the data attributes with these values.
8
9 def _ _init_ _(self, make, model, mileage, price):
10 self._ _make = make

(program continues)
586 Chapter 11  Inheritance

Program 11-1 (continued)

11 self._ _model = model


12 self._ _mileage = mileage
13 self._ _price = price
14
15 # The following methods are mutators for the
16 # class's data attributes.
17
18 def set_make(self, make):
19 self._ _make = make
20
21 def set_model(self, model):
22 self._ _model = model
23
24 def set_mileage(self, mileage):
25 self._ _mileage = mileage
26
27 def set_price(self, price):
28 self._ _price = price
29
30 # The following methods are the accessors
31 # for the class's data attributes.
32
33 def get_make(self):
34 return self._ _make
35
36 def get_model(self):
37 return self._ _model
38
39 def get_mileage(self):
40 return self._ _mileage
41
42 def get_price(self):
43 return self._ _price
44

The Automobile class’s _ _init_ _ method accepts arguments for the vehicle’s make,
model, mileage, and price. It uses those values to initialize the following data attributes:
• _ _make
• _ _model
• _ _mileage
• _ _price

(Recall from Chapter 10 that a data attribute becomes hidden when its name begins with
two underscores.) The methods that appear in lines 18 through 28 are ­mutators for each of
the data attributes, and the methods in lines 33 through 43 are the accessors.
11.1 Introduction to Inheritance 587

The Automobile class is a complete class from which we can create objects. If we wish,
we can write a program that imports the vehicle module and creates instances of the
Automobile class. However, the Automobile class holds only general data about an auto-
mobile. It does not hold any of the specific pieces of data that the dealership wants to keep
about cars, pickup trucks, and SUVs. To hold data about those specific types of automo-
biles, we will write subclasses that inherit from the Automobile class. Program 11-2 shows
the code for the Car class, which is also in the vehicles module.

Program 11-2 (Lines 45 through 72 of vehicles.py)

45 # The Car class represents a car. It is a subclass


46 # of the Automobile class.
47
48 class Car(Automobile):
49 # The _ _init_ _ method accepts arguments for the
50 # car's make, model, mileage, price, and doors.
51
52 def _ _init_ _(self, make, model, mileage, price, doors):
53 # Call the superclass's _ _init_ _ method and pass
54 # the required arguments. Note that we also have
55 # to pass self as an argument.
56 Automobile._ _init_ _(self, make, model, mileage, price)
57
58 # Initialize the _ _doors attribute.
59 self._ _doors = doors
60
61 # The set_doors method is the mutator for the
62 # _ _doors attribute.
63
64 def set_doors(self, doors):
65 self._ _doors = doors
66
67 # The get_doors method is the accessor for the
68 # _ _doors attribute.
69
70 def get_doors(self):
71 return self._ _doors
72

Take a closer look at the first line of the class declaration, in line 48:
class Car(Automobile):
This line indicates that we are defining a class named Car, and it inherits from the Automobile
class. The Car class is the subclass, and the Automobile class is the superclass. If we want
to express the relationship between the Car class and the Automobile class, we can say that
a Car is an Automobile. Because the Car class extends the Automobile class, it inherits all
of the methods and data attributes of the Automobile class.
588 Chapter 11  Inheritance

Look at the header for the _ _init_ _ method in line 52:


def _ _init_ _(self, make, model, mileage, price, doors):

Notice in addition to the required self parameter, the method has parameters named make,
model, mileage, price, and doors. This makes sense because a Car object will have data
attributes for the car’s make, model, mileage, price, and number of doors. Some of these
attributes are created by the Automobile class, however, so we need to call the Automobile
class’s _ _init_ _ method and pass those values to it. That happens in line 56:
Automobile._ _init_ _(self, make, model, mileage, price)

This statement calls the Automobile class’s _ _init_ _ method. Notice the statement passes
the self variable, as well as the make, model, mileage, and price variables as arguments.
When that method executes, it initializes the _ _make, _ _model, _ _mileage, and _ _price
data attributes. Then, in line 59, the _ _doors attribute is initialized with the value passed
into the doors parameter:
self._ _doors = doors

The set_doors method, in lines 64 through 65, is the mutator for the _ _doors attribute,
and the get_doors method, in lines 70 through 71, is the accessor for the _ _doors attri-
bute. Before going any further, let’s demonstrate the Car class, as shown in Program 11-3.

Program 11-3 (car_demo.py)

1 # This program demonstrates the Car class.


2
3 import vehicles
4
5 def main():
6 # Create an object from the Car class.
7 # The car is a 2007 Audi with 12,500 miles, priced
8 # at $21,500.00, and has 4 doors.
9 used_car = vehicles.Car('Audi', 2007, 12500, 21500.0, 4)
10
11 # Display the car's data.
12 print('Make:', used_car.get_make())
13 print('Model:', used_car.get_model())
14 print('Mileage:', used_car.get_mileage())
15 print('Price:', used_car.get_price())
16 print('Number of doors:', used_car.get_doors())
17
18 # Call the main function.
19 if _ _name_ _ == '_ _main_ _':
20 main()

Program Output
Make: Audi
Model: 2007
11.1 Introduction to Inheritance 589

Mileage: 12500
Price: 21500.0
Number of doors: 4

Line 3 imports the vehicles module, which contains the class definitions for the
Automobile and Car classes. Line 9 creates an instance of the Car class, passing 'Audi' as
the car’s make, 2007 as the car’s model, 12,500 as the mileage, 21,500.0 as the car’s price,
and 4 as the number of doors. The resulting object is assigned to the used_car variable.
The statements in lines 12 through 15 call the object’s get_make, get_model, get_­mileage,
and get_price methods. Even though the Car class does not have any of these methods,
it inherits them from the Automobile class. Line 16 calls the get_doors method, which is
defined in the Car class.
Now let’s look at the Truck class, which also inherits from the Automobile class. The code
for the Truck class, which is also in the vehicles module, is shown in Program 11-4.

Program 11-4 (Lines 73 through 100 of vehicles.py)

73 # The Truck class represents a pickup truck. It is a


74 # subclass of the Automobile class.
75
76 class Truck(Automobile):
77 # The _ _init_ _ method accepts arguments for the
78 # Truck's make, model, mileage, price, and drive type.
79
80 def _ _init_ _(self, make, model, mileage, price, drive_type):
81 # Call the superclass's _ _init_ _ method and pass
82 # the required arguments. Note that we also have
83 # to pass self as an argument.
84 Automobile._ _init_ _(self, make, model, mileage, price)
85
86 # Initialize the _ _drive_type attribute.
87 self._ _drive_type = drive_type
88
89 # The set_drive_type method is the mutator for the
90 # _ _drive_type attribute.
91
92 def set_drive_type(self, drive_type):
93 self._ _drive = drive_type
94
95 # The get_drive_type method is the accessor for the
96 # _ _drive_type attribute.
97
98 def get_drive_type(self):
99 return self._ _drive_type
100
590 Chapter 11  Inheritance

The Truck class’s _ _init_ _ method begins in line 80. Notice it takes arguments for the
truck’s make, model, mileage, price, and drive type. Just as the Car class did, the Truck
class calls the Automobile class’s _ _init_ _ method (in line 84) passing the make, model,
mileage, and price as arguments. Line 87 creates the _ _drive_type attribute, initializing
it to the value of the drive_type parameter.
The set_drive_type method in lines 92 through 93 is the mutator for the _ _drive_type
attribute, and the get_drive_type method in lines 98 through 99 is the accessor for the
attribute.
Now let’s look at the SUV class, which also inherits from the Automobile class. The code
for the SUV class, which is also in the vehicles module, is shown in Program 11-5.

Program 11-5 (Lines 101 through 128 of vehicles.py)

101 # The SUV class represents a sport utility vehicle. It


102 # is a subclass of the Automobile class.
103
104 class SUV(Automobile):
105 # The _ _init_ _ method accepts arguments for the
106 # SUV's make, model, mileage, price, and passenger
107 # capacity.
108
109 def _ _init_ _(self, make, model, mileage, price, pass_cap):
110 # Call the superclass's _ _init_ _ method and pass
111 # the required arguments. Note that we also have
112 # to pass self as an argument.
113 Automobile._ _init_ _(self, make, model, mileage, price)
114
115 # Initialize the _ _pass_cap attribute.
116 self._ _pass_cap = pass_cap
117
118 # The set_pass_cap method is the mutator for the
119 # _ _pass_cap attribute.
120
121 def set_pass_cap(self, pass_cap):
122 self._ _pass_cap = pass_cap
123
124 # The get_pass_cap method is the accessor for the
125 # _ _pass_cap attribute.
126
127 def get_pass_cap(self):
128 return self._ _pass_cap

The SUV class’s _ _init_ _ method begins in line 109. It takes arguments for the vehicle’s
make, model, mileage, price, and passenger capacity. Just as the Car and Truck classes
did, the SUV class calls the Automobile class’s _ _init_ _ method (in line 113) passing the
11.1 Introduction to Inheritance 591

make, model, mileage, and price as arguments. Line 116 creates the _ _pass_cap attribute,
initializing it to the value of the pass_cap parameter.
The set_pass_cap method in lines 121 through 122 is the mutator for the _ _pass_cap
attribute, and the get_pass_cap method in lines 127 through 128 is the accessor for the
attribute.
Program 11-6 demonstrates each of the classes we have discussed so far. It creates a Car
object, a Truck object, and an SUV object.

Program 11-6 (car_truck_suv_demo.py)

1 # This program creates a Car object, a Truck object,


2 # and an SUV object.
3
4 import vehicles
5
6 def main():
7 # Create a Car object for a used 2001 BMW
8 # with 70,000 miles, priced at $15,000, with
9 # 4 doors.
10 car = vehicles.Car('BMW', 2001, 70000, 15000.0, 4)
11
12 # Create a Truck object for a used 2002
13 # Toyota pickup with 40,000 miles, priced
14 # at $12,000, with 4-wheel drive.
15 truck = vehicles.Truck('Toyota', 2002, 40000, 12000.0, '4WD')
16
17 # Create an SUV object for a used 2000
18 # Volvo with 30,000 miles, priced
19 # at $18,500, with 5 passenger capacity.
20 suv = vehicles.SUV('Volvo', 2000, 30000, 18500.0, 5)
21
22 print('USED CAR INVENTORY')
23 print('===================')
24
25 # Display the car's data.
26 print('The following car is in inventory:')
27 print('Make:', car.get_make())
28 print('Model:', car.get_model())
29 print('Mileage:', car.get_mileage())
30 print('Price:', car.get_price())
31 print('Number of doors:', car.get_doors())
32 print()
33
34 # Display the truck's data.
35 print('The following pickup truck is in inventory.')
36 print('Make:', truck.get_make())
(program continues)
592 Chapter 11  Inheritance

Program 11-6 (continued)

37 print('Model:', truck.get_model())
38 print('Mileage:', truck.get_mileage())
39 print('Price:', truck.get_price())
40 print('Drive type:', truck.get_drive_type())
41 print()
42
43 # Display the SUV's data.
44 print('The following SUV is in inventory.')
45 print('Make:', suv.get_make())
46 print('Model:', suv.get_model())
47 print('Mileage:', suv.get_mileage())
48 print('Price:', suv.get_price())
49 print('Passenger Capacity:', suv.get_pass_cap())
50
51 # Call the main function.
52 if _ _name_ _ == '_ _main_ _':
53 main()

Program Output
USED CAR INVENTORY
==================
The following car is in inventory:
Make: BMW
Model: 2001
Mileage: 70000
Price: 15000.0
Number of doors: 4

The following pickup truck is in inventory.


Make: Toyota
Model: 2002
Mileage: 40000
Price: 12000.0
Drive type: 4WD

The following SUV is in inventory.


Make: Volvo
Model: 2000
Mileage: 30000
Price: 18500.0
Passenger Capacity: 5

Inheritance in UML Diagrams


You show inheritance in a UML diagram by drawing a line with an open arrowhead from the
subclass to the superclass. (The arrowhead points to the superclass.) Figure 11-2 is a UML
diagram showing the relationship between the Automobile, Car, Truck, and SUV classes.
11.1 Introduction to Inheritance 593

Figure 11-2 UML diagram showing inheritance

Automobile

__make
__model
__mileage
__price

__init__(make, model,
mileage, price)
set_make(make)
set_model(model)
set_mileage(mileage)
set_price(price)
get_make( )
get_model( )
get_mileage( )
get_price()

Car Truck SUV

__doors __drive_type __pass_cap

__init__(make, model, __init__(make, model, __init__(make, model,


mileage, price, doors) mileage, price, drive_type) mileage, price, pass_cap)
set_doors(doors) set_drive_type(drive_type) set_pass_cap(pass_cap)
get_doors() get_drive_type() get_pass_cap()

In the Spotlight:
Using Inheritance
Bank Financial Systems, Inc. develops financial software for banks and credit unions. The
company is developing a new object-oriented system that manages customer accounts. One
of your tasks is to develop a class that represents a savings account. The data that must be
held by an object of this class is:
• The account number.
• The interest rate.
• The account balance.
You must also develop a class that represents a certificate of deposit (CD) account. The data
that must be held by an object of this class is:
• The account number.
• The interest rate.
• The account balance.
• The account maturity date.
594 Chapter 11  Inheritance

As you analyze these requirements, you realize that a CD account is really a specialized
version of a savings account. The class that represents a CD will hold all of the same data
as the class that represents a savings account, plus an extra attribute for the maturity date.
You decide to design a SavingsAccount class to represent a savings account, then design
a subclass of SavingsAccount named CD to represent a CD account. You will store both
of these classes in a module named accounts . Program 11-7 shows the code for the
SavingsAccount class.

Program 11-7 (Lines 1 through 37 of accounts.py)

1 # The SavingsAccount class represents a


2 # savings account.
3
4 class SavingsAccount:
5
6 # The _ _init_ _ method accepts arguments for the
7 # account number, interest rate, and balance.
8
9 def _ _init_ _(self, account_num, int_rate, bal):
10 self._ _account_num = account_num
11 self._ _interest_rate = int_rate
12 self._ _balance = bal
13
14 # The following methods are mutators for the
15 # data attributes.
16
17 def set_account_num(self, account_num):
18 self._ _account_num = account_num
19
20 def set_interest_rate(self, int_rate):
21 self._ _interest_rate = int_rate
22
23 def set_balance(self, bal):
24 self._ _balance = bal
25
26 # The following methods are accessors for the
27 # data attributes.
28
29 def get_account_num(self):
30 return self._ _account_num
31
32 def get_interest_rate(self):
33 return self._ _interest_rate
34
35 def get_balance(self):
36 return self._ _balance
37
11.1 Introduction to Inheritance 595

The class’s _ _init_ _ method appears in lines 9 through 12. The _ _init_ _ method accepts
arguments for the account number, interest rate, and balance. These arguments are used to
initialize data attributes named _ _account_num, _ _interest_rate, and _ _­balance.
The set_account_num , set_interest_rate , and set_balance methods that ap-
pear in lines 17 through 24 are mutators for the data attributes. The get_account_num,
get_interest_rate, and get_balance methods that appear in lines 29 through 36 are
accessors.
The CD class is shown in the next part of Program 11-7.

Program 11-7 (Lines 38 through 65 of accounts.py)

38 # The CD account represents a certificate of


39 # deposit (CD) account. It is a subclass of
40 # the SavingsAccount class.
41
42 class CD(SavingsAccount):
43
44 # The init method accepts arguments for the
45 # account number, interest rate, balance, and
46 # maturity date.
47
48 def _ _init_ _(self, account_num, int_rate, bal, mat_date):
49 # Call the superclass _ _init_ _ method.
50 SavingsAccount._ _init_ _(self, account_num, int_rate, bal)
51
52 # Initialize the _ _maturity_date attribute.
53 self._ _maturity_date = mat_date
54
55 # The set_maturity_date is a mutator for the
56 # _ _maturity_date attribute.
57
58 def set_maturity_date(self, mat_date):
59 self._ _maturity_date = mat_date
60
61 # The get_maturity_date method is an accessor
62 # for the _ _maturity_date attribute.
63
64 def get_maturity_date(self):
65 return self._ _maturity_date

The CD class’s _ _init_ _ method appears in lines 48 through 53. It accepts arguments
for the account number, interest rate, balance, and maturity date. Line 50 calls the
SavingsAccount class’s _ _init_ _ method, passing the arguments for the account number,
interest rate, and balance. After the SavingsAccount class’s _ _init_ _ method executes,
the _ _account_num, _ _interest_rate, and _ _balance attributes will be created and
initialized. Then the statement in line 53 creates the _ _maturity_date attribute.
596 Chapter 11  Inheritance

The set_maturity_date method in lines 58 through 59 is the mutator for the


_ _maturity_date attribute, and the get_maturity_date method in lines 64 through 65
is the accessor.
To test the classes, we use the code shown in Program 11-8. This program creates an in-
stance of the SavingsAccount class to represent a savings account, and an instance of the
CD account to represent a certificate of deposit account.

Program 11-8 (account_demo.py)

1 # This program creates an instance of the SavingsAccount


2 # class and an instance of the CD account.
3
4 import accounts
5
6 def main():
7 # Get the account number, interest rate,
8 # and account balance for a savings account.
9 print('Enter the following data for a savings account.')
10 acct_num = input('Account number: ')
11 int_rate = float(input('Interest rate: '))
12 balance = float(input('Balance: '))
13
14 # Create a SavingsAccount object.
15 savings = accounts.SavingsAccount(acct_num, int_rate,
16 balance)
17
18 # Get the account number, interest rate,
19 # account balance, and maturity date for a CD.
20 print('Enter the following data for a CD.')
21 acct_num = input('Account number: ')
22 int_rate = float(input('Interest rate: '))
23 balance = float(input('Balance: '))
24 maturity = input('Maturity date: ')
25
26 # Create a CD object.
27 cd = accounts.CD(acct_num, int_rate, balance, maturity)
28
29 # Display the data entered.
30 print('Here is the data you entered:')
31 print()
32 print('Savings Account')
33 print('---------------')
34 print(f'Account number: {savings.get_account_num()}')
35 print(f'Interest rate: {savings.get_interest_rate()}')
36 print(f'Balance: ${savings.get_balance():,.2f}')
11.1 Introduction to Inheritance 597

37 print()
38 print('CD')
39 print('---------------')
40 print(f'Account number: {cd.get_account_num()}')
41 print(f'Interest rate: {cd.get_interest_rate()}')
42 print(f'Balance: ${cd.get_balance():,.2f}')
43 print(f'Maturity date: {cd.get_maturity_date()}')
44
45 # Call the main function.
46 if _ _name_ _ == '_ _main_ _':
47 main()

Program Output (with input shown in bold)


Enter the following data for a savings account.
Account number: 1234SA Enter
Interest rate: 3.5 Enter
Balance: 1000.00 Enter
Enter the following data for a CD.
Account number: 2345CD Enter
Interest rate: 5.6 Enter
Balance: 2500.00 Enter
Maturity date: 12/12/2024 Enter
Here is the data you entered:

Savings Account
---------------
Account number: 1234SA
Interest rate: 3.5
Balance: $1,000.00

CD
---------------
Account number: 2345CD
Interest rate: 5.6
Balance: $2,500.00
Maturity date: 12/12/2024

Checkpoint
11.1 In this section, we discussed superclasses and subclasses. Which is the general
class, and which is the specialized class?
11.2 What does it mean to say there is an “is a” relationship between two objects?
11.3 What does a subclass inherit from its superclass?
11.4 Look at the following code, which is the first line of a class definition. What is the
name of the superclass? What is the name of the subclass?
class Canary(Bird):
598 Chapter 11  Inheritance

11.2 Polymorphism

CONCEPT: Polymorphism allows subclasses to have methods with the same names
as methods in their superclasses. It gives the ability for a program to call
the correct method depending on the type of object that is used to call it.

The term polymorphism refers to an object’s ability to take different forms. It is a power-
ful feature of object-oriented programming. In this section, we will look at two essential
ingredients of polymorphic behavior:
1. The ability to define a method in a superclass, then define a method with the same
name in a subclass. When a subclass method has the same name as a superclass
method, it is often said that the subclass method overrides the superclass method.
2. The ability to call the correct version of an overridden method, depending on the
type of object that is used to call it. If a subclass object is used to call an overridden
method, then the subclass’s version of the method is the one that will execute. If a
superclass object is used to call an overridden method, then the superclass’s version
of the method is the one that will execute.
Actually, you’ve already seen method overriding at work. Each subclass that we have exam-
ined in this chapter has a method named _ _init_ _ that overrides the superclass’s _ _init_ _
method. When an instance of the subclass is created, it is the subclass’s _ _init_ _ method
that automatically gets called.
Method overriding works for other class methods too. Perhaps the best way to describe
polymorphism is to demonstrate it, so let’s look at a simple example. Program 11-9 shows
the code for a class named Mammal, which is in a module named animals.

Program 11-9 (Lines 1 through 22 of animals.py)

1 # The Mammal class represents a generic mammal.


2
3 class Mammal:
4
5 # The _ _init_ _ method accepts an argument for
6 # the mammal's species.
7
8 def _ _init_ _(self, species):
9 self._ _species = species
10
11 # The show_species method displays a message
12 # indicating the mammal's species.
13
14 def show_species(self):
15 print('I am a', self._ _species)
16
17 # The make_sound method is the mammal's
18 # way of making a generic sound.
11.2 Polymorphism 599

19
20 def make_sound(self):
21 print('Grrrrr')
22

The Mammal class has three methods: _ _init_ _, show_species and make_sound. Here is
an example of code that creates an instance of the class and calls these methods:
import animals
mammal = animals.Mammal('regular mammal')
mammal.show_species()
mammal.make_sound()

This code will display the following:


I am a regular mammal
Grrrrr

The next part of Program 11-9 shows the Dog class. The Dog class, which is also in the
animals module, is a subclass of the Mammal class.

Program 11-9 (Lines 23 through 38 of animals.py)

23 # The Dog class is a subclass of the Mammal class.


24
25 class Dog(Mammal):
26
27 # The _ _init_ _ method calls the superclass's
28 # _ _init_ _ method passing 'Dog' as the species.
29
30 def _ _init_ _(self):
31 Mammal._ _init_ _(self, 'Dog')
32
33 # The make_sound method overrides the superclass's
34 # make_sound method.
35
36 def make_sound(self):
37 print('Woof! Woof!')
38

Even though the Dog class inherits the _ _init_ _ and make_sound methods that are in the
Mammal class, those methods are not adequate for the Dog class. So, the Dog class has its
own _ _init_ _ and make_sound methods, which perform actions that are more appropri-
ate for a dog. We say that the _ _init_ _ and make_sound methods in the Dog class override
the _ _init_ _ and make_sound methods in the Mammal class. Here is an example of code
that creates an instance of the Dog class and calls the methods:
import animals
dog = animals.Dog()
600 Chapter 11  Inheritance

dog.show_species()
dog.make_sound()

This code will display the following:


I am a Dog
Woof! Woof!

When we use a Dog object to call the show_species and make_sound methods, the ver-
sions of these methods that are in the Dog class are the ones that execute. Next, look at
Program 11-10, which shows the Cat class. The Cat class, which is also in the animals
module, is another subclass of the Mammal class.

Program 11-9 (Lines 39 through 53 of animals.py)

39 # The Cat class is a subclass of the Mammal class.


40
41 class Cat(Mammal):
42
43 # The _ _init_ _ method calls the superclass's
44 # _ _init_ _ method passing 'Cat' as the species.
45
46 def _ _init_ _(self):
47 Mammal._ _init_ _(self, 'Cat')
48
49 # The make_sound method overrides the superclass's
50 # make_sound method.
51
52 def make_sound(self):
53 print('Meow')

The Cat class also overrides the Mammal class’s _ _init_ _ and make_sound methods. Here
is an example of code that creates an instance of the Cat class and calls these methods:
import animals
cat = animals.Cat()
cat.show_species()
cat.make_sound()

This code will display the following:


I am a Cat
Meow

When we use a Cat object to call the show_species and make_sound methods, the ver-
sions of these methods that are in the Cat class are the ones that execute.
11.2 Polymorphism 601

The isinstance Function


Polymorphism gives us a great deal of flexibility when designing programs. For example,
look at the following function:
def show_mammal_info(creature):
creature.show_species()
creature.make_sound()

We can pass any object as an argument to this function, and as long as it has a show_­
species method and a make_sound method, the function will call those methods. In
essence, we can pass any object that “is a” Mammal (or a subclass of Mammal) to the func-
tion. Program 11-10 demonstrates.

Program 11-10 (polymorphism_demo.py)

1 # This program demonstrates polymorphism.


2
3 import animals
4
5 def main():
6 # Create a Mammal object, a Dog object, and
7 # a Cat object.
8 mammal = animals.Mammal('regular animal')
9 dog = animals.Dog()
10 cat = animals.Cat()
11
12 # Display information about each one.
13 print('Here are some animals and')
14 print('the sounds they make.')
15 print('--------------------------')
16 show_mammal_info(mammal)
17 print()
18 show_mammal_info(dog)
19 print()
20 show_mammal_info(cat)
21
22 # The show_mammal_info function accepts an object
23 # as an argument, and calls its show_species
24 # and make_sound methods.
25
26 def show_mammal_info(creature):
27 creature.show_species()
28 creature.make_sound()
29
30 # Call the main function.
31 if _ _name_ _ == '_ _main_ _':
32 main()
(program output continues)
602 Chapter 11  Inheritance

Program Output (continued)


Here are some animals and
the sounds they make.
--------------------------
I am a regular animal
Grrrrr
I am a Dog
Woof! Woof!
I am a Cat
Meow

But what happens if we pass an object that is not a Mammal, and not of a subclass of Mammal
to the function? For example, what will happen when Program 11-11 runs?

Program 11-11 (wrong_type.py)

1 def main():
2 # Pass a string to show_mammal_info …
3 show_mammal_info('I am a string')
4
5 # The show_mammal_info function accepts an object
6 # as an argument, and calls its show_species
7 # and make_sound methods.
8
9 def show_mammal_info(creature):
10 creature.show_species()
11 creature.make_sound()
12
13 # Call the main function.
14 if _ _name_ _ == '_ _main_ _':
15     main()

In line 3, we call the show_mammal_info function passing a string as an argument. When


the interpreter attempts to execute line 10, however, an AttributeError exception will be
raised because strings do not have a method named show_species.
We can prevent this exception from occurring by using the built-in function isinstance.
You can use the isinstance function to determine whether an object is an instance of a
specific class, or a subclass of that class. Here is the general format of the function call:
isinstance(object, ClassName)
In the general format, object is a reference to an object, and ClassName is the name of a
class. If the object referenced by object is an instance of ClassName or is an instance of a
subclass of ClassName, the function returns true. Otherwise, it returns false. Program 11-12
shows how we can use it in the show_mammal_info function.
11.2 Polymorphism 603

Program 11-12 (polymorphism_demo2.py)

1 # This program demonstrates polymorphism.


2
3 import animals
4
5 def main():
6 # Create an Mammal object, a Dog object, and
7 # a Cat object.
8 mammal = animals.Mammal('regular animal')
9 dog = animals.Dog()
10 cat = animals.Cat()
11
12 # Display information about each one.
13 print('Here are some animals and')
14 print('the sounds they make.')
15 print('--------------------------')
16 show_mammal_info(mammal)
17 print()
18 show_mammal_info(dog)
19 print()
20 show_mammal_info(cat)
21 print()
22 show_mammal_info('I am a string')
23
24 # The show_mammal_info function accepts an object
25 # as an argument and calls its show_species
26 # and make_sound methods.
27
28 def show_mammal_info(creature):
29 if isinstance(creature, animals.Mammal):
30 creature.show_species()
31 creature.make_sound()
32 else:
33 print('That is not a Mammal!')
34
35 # Call the main function.
36 if _ _name_ _ == '_ _main_ _':
37        main()

Program Output
Here are some animals and
the sounds they make.
--------------------------
I am a regular animal
Grrrrr
(program output continues)
604 Chapter 11  Inheritance

Program Output (continued)


I am a Dog
Woof! Woof!
I am a Cat
Meow
That is not a Mammal!

In lines 16, 18, and 20 we call the show_mammal_info function, passing references to a
Mammal object, a Dog object, and a Cat object. In line 22, however, we call the function and
pass a string as an argument. Inside the show_mammal_info function, the if statement in
line 29 calls the isinstance function to determine whether the argument is an instance of
Mammal (or a subclass). If it is not, an error message is displayed.

Checkpoint
11.5 Look at the following class definitions:
class Vegetable:
def _ _init_ _(self, vegtype):
self._ _vegtype = vegtype
def message(self):
print("I'm a vegetable.")
class Potato(Vegetable):
def _ _init_ _(self):
Vegetable._ _init_ _(self, 'potato')
def message(self):
print("I'm a potato.")
Given these class definitions, what will the following statements display?
v = Vegetable('veggie')
p = Potato()
v.message()
p.message()

Review Questions
Multiple Choice
1. In an inheritance relationship, the __________ is the general class.
a. subclass
b. superclass
c. slave class
d. child class
Review Questions 605

2. In an inheritance relationship, the __________ is the specialized class.


a. superclass
b. master class
c. subclass
d. parent class
3. Suppose a program uses two classes: Airplane and JumboJet. Which of these
would most likely be the subclass?
a. Airplane
b. JumboJet
c. Both
d. Neither
4. This characteristic of object-oriented programming allows the correct version of an
overridden method to be called when an instance of a subclass is used to call it.
a. polymorphism
b. inheritance
c. generalization
d. specialization
5. You can use this to determine whether an object is an instance of a class.
a. the in operator
b. the is_object_of function
c. the isinstance function
d. the error messages that are displayed when a program crashes

True or False
1. Polymorphism allows you to write methods in a subclass that have the same name as
methods in the superclass.
2. It is not possible to call a superclass’s _ _init_ _ method from a subclass’s
_ _init_ _ method.
3. A subclass can have a method with the same name as a method in the superclass.
4. Only the _ _init_ _ method can be overridden.
5. You cannot use the isinstance function to determine whether an object is an
instance of a subclass of a class.

Short Answer
1. What does a subclass inherit from its superclass?
2. Look at the following class definition. What is the name of the superclass? What is
the name of the subclass?
class Tiger(Felis):
3. What is an overridden method?

Algorithm Workbench
1. Write the first line of the definition for a Poodle class. The class should extend the
Dog class.
606 Chapter 11  Inheritance

2. Look at the following class definitions:


class Plant:
def _ _init_ _(self, plant_type):
self._ _plant_type = plant_type
def message(self):
print("I'm a plant.")
class Tree(Plant):
def _ _init_ _(self):
Plant._ _init_ _(self, 'tree')
def message(self):
print("I'm a tree.")

Given these class definitions, what will the following statements display?
p = Plant('sapling')
t = Tree()
p.message()
t.message()
3. Look at the following class definition:
class Beverage:
def _ _init_ _(self, bev_name):
self._ _bev_name = bev_name

Write the code for a class named Cola that is a subclass of the Beverage class. The
Cola class’s _ _init_ _ method should call the Beverage class’s _ _init_ _ method,
passing ‘cola’ as an argument.

Programming Exercises
1. Employee and ProductionWorker Classes
Write an Employee class that keeps data attributes for the following pieces of information:
• Employee name
• Employee number
Next, write a class named ProductionWorker that is a subclass of the Employee class.
The ProductionWorker class should keep data attributes for the following information:
• Shift number (an integer, such as 1, 2, or 3)
• Hourly pay rate
The workday is divided into two shifts: day and night. The shift attribute will hold an
integer value representing the shift that the employee works. The day shift is shift 1 and the
night shift is shift 2. Write the appropriate accessor and mutator methods for each class.
Once you have written the classes, write a program that creates an object of the
ProductionWorker class and prompts the user to enter data for each of the object’s data
attributes. Store the data in the object, then use the object’s accessor methods to retrieve it
and display it on the screen.
Programming Exercises 607

2. ShiftSupervisor Class
In a particular factory, a shift supervisor is a salaried employee who supervises a shift. In
addition to a salary, the shift supervisor earns a yearly bonus when his or her shift meets
production goals. Write a ShiftSupervisor class that is a subclass of the Employee class
you created in Programming Exercise 1. The ShiftSupervisor class should keep a data
attribute for the annual salary, and a data attribute for the annual production bonus that
a shift supervisor has earned. Demonstrate the class by writing a program that uses a
ShiftSupervisor object.

3. Person and Customer Classes


VideoNote
Write a class named Person with data attributes for a person’s name, address, and tel-
The Person and ephone number. Next, write a class named Customer that is a subclass of the Person class.
Customer Classes
The Customer class should have a data attribute for a customer number, and a Boolean
data attribute indicating whether the customer wishes to be on a mailing list. Demonstrate
an instance of the Customer class in a simple program.

You might also like