Py Tutorial 85 112
Py Tutorial 85 112
GRAPHICS 85
main()
The only part of this with new ideas is:
entry1 = Entry(Point(winWidth/2, 200),10)
entry1.draw(win)
Text(Point(winWidth/2, 230),’Name:’).draw(win) # label for the Entry
def main():
winWidth = 300
winHeight = 300
numStr1 = entry1.getText()
num1 = int(numStr1)
numStr2 = entry2.getText()
num2 = int(numStr2)
result = "The sum of\n{num1}\nplus\n{num2}\nis {sum}.".format(**locals())
Text(Point(winWidth/2, 110), result).draw(win)
win.getMouse()
win.close()
main()
There is not a separate graphical replacement for the input statement, so you only can read strings. With
conversions, it is still possible to work with numbers.
Only one new graphical method has been included above:
entry1.setText(’0’)
Again the same method name is used as with a Text object. In this case I chose not to leave the Entry
initially blank. The 0 value also reinforces that a numerical value is expected.
There is also an entry2 with almost identical code. After waiting for a mouse click, both entries are
read, and the chosen names emphasizes they are strings. The strings must be converted to integers in order
to do arithmetic and display the result.
The almost identical code for the two entries is a strong suggestion that this code could be written more
easily with a function. You may look at the identically functioning example program addEntries2.py. The
only changes are shown below. First there is a function to create an Entry and a centered static label over
it.
2.4.10. Color Names. Thus far we have only used common color names. In fact there are a very large
number of allowed color names, and the abiltity to draw with custom colors.
2.4. GRAPHICS 87
First, the graphics package is built on an underlying graphics system, Tkinter, which has a large number
of color names defined. Each of the names can be used by itself, like ’red’, ’salmon’ or ’aquamarine’ or with
a lower intensity by specifying with a trailing number 2, 3, or 4, like ’red4’ for a dark red.
Though the ideas for the coding have not all been introduced, it is still informative to run the example
program colors.py. As you click the mouse over and over, you see the names and appearances of a wide
variety of built-in color names. The names must be place in quotes, but capitalization is ignored.
2.4.11. Custom Colors. Custom colors can also be created. To do that requires some understanding
of human eyes and color (and the Python tools). The only colors detected directly by the human eyes are
red, green, and blue. Each amount is registered by a different kind of cone cell in the retina. As far as the
eye is concerned, all the other colors we see are just combinations of these three colors. This fact is used
in color video screens: they only directly display these three colors. A common scale to use in labeling the
intensity of each of the basic colors (red, green, blue) is from 0 to 255, with 0 meaning none of the color, and
255 being the most intense. Hence a color can be described by a sequence of red, green, and blue intensities
(often abbreviated RGB). The graphics package has a function, color_rgb, to create colors this way. For
instance a color with about half the maximum red intensity, no green, and maximum blue intensity would
be
aColor = color_rgb(128, 0, 255)
Such a creation can be used any place a color is used in the graphics, (i.e. circle.setFill(aColor) ).
2.4.12. Random Colors. Another interesting use of the color_rgb function is to create random
colors. Run example program randomCircles.py. The code also is here:
"""Draw random circles.
"""
def main():
win = GraphWin("Random Circles", 300, 300)
for i in range(75):
r = random.randrange(256)
b = random.randrange(256)
g = random.randrange(256)
color = color_rgb(r, g, b)
main()
Read the fragments of this program and their explanations:
To do random things, the program needs a function from the random module. This example shows that
imported modules may be put in a comma separated list:
import random, time
2.5. FILES 88
You have already seen the built-in function range. To generate a sequence of all the integers 0, 1, ... 255,
you would use
range(256)
This is the full list of possible values for the red, green or blue intensity parameter. For this program
we randomly choose any one element from this sequence. Instead of the range function, use the random
module’s randrange function, as in
r = random.randrange(256)
b = random.randrange(256)
g = random.randrange(256)
color = color_rgb(r, g, b)
This gives randomly selected values to each of r, g, and b, which are then used to create the random color.
I want a random circle radius, but I do not want a number as small as 0, making it invisible. The range
and randrange functions both refer to a possible sequence of values starting with 0 when a single parameter
is used. It is also possible to add a different starting value as the first parameter. You still must specify a
value past the end of the sequence. For instance
range(3, 40)
would refer to the sequence 3, 4, 5, ... , 39 (starting with 3 and not quite reaching 40). Similarly
random.randrange(3, 40)
randomly selects an arbitrary element of range(3, 40).
I use the two-parameter version to select random parameters for a Circle:
radius = random.randrange(3, 40)
x = random.randrange(5, 295)
y = random.randrange(5, 295)
2.5. Files
This section fits here logically (as an important built-in type of object) but it is not needed for the next
chapter, Flow of Control, 3.
Thus far you have been able to save programs, but anything produced during the execution of a program
has been lost when the program ends. Data has not persisted past the end of execution. Just as programs
live on in files, you can generate and read data files in Python that persist after your program has finished
running.
As far as Python is concerned, a file is just a string (often very large!) stored on your file system, that
you can read or write gradually or all together.
Open a directory window for your Python program directory. First note that there is no file named
sample.txt.
Make sure you have started Idle so the current directory is your Python program directory (for instance
in Windows with the downloaded shortcut to Idle).
Run the example program firstFile.py, shown below:
outFile = open(’sample.txt’, ’w’)
outFile.write(’My first output file!’)
outFile.close()
75 and 294 (one less than 295).
8If 4 or n is the last number, what is the first number past the end of the sequence?
2.5. FILES 89
The first line creates a file object, which links Python to your computer’s file system. The first parameter in
the file constructor gives the file name, sample.txt. The second parameter indicates how you use the file.
The ’w’ is short for write, so you will be creating and writing to a file (or if it already existed, destroying
the old contents and starting over!). If you do not use any operating system directory separators in the
name (’\’ or ’/’ depending on your operating system), then the file will lie in the current directory. The
assignment statement gives the python file object the name outFile.
The second line writes the specified string to the file.
The last line is important to clean up. Until this line, this Python program controls the file, and nothing
may even be actually written to the file. (Since file operations are thousands of times slower than memory
operations, Python buffers data, saving small amounts and writing all at once in larger chunks.) The close
line is essential for Python to make sure everything is really written, and to relinquish control of the file. It
is a common bug to write a program where you have the code to add all the data you want to a file, but the
program does not end up creating a file. Usually this means you forgot to close the file.
Now switch focus and look at a file window for the current directory. You should now see a file
sample.txt. You can open it in Idle (or your favorite word processor) and see its contents.
Run the example program nextFile.py, shown below, which has two calls to the write method:
outFile = open(’sample2.txt’, ’w’)
outFile.write(’My second output file!
outFile.write(’Write some more.’)
outFile.close()
Now look at the file, sample2.txt. Open it in Idle. It may not be what you expect! The write method for
the file is not quite like a print function. It does not add anything to the file except exactly the data you
tell it to write. If you want a newline, you must indicate it explicitly. Recall the newline code \n. Run the
example program revisedFile.py, shown below, which adds newline codes:
outFile = open(’sample3.txt’, ’w’)
outFile.write(’A revised output file!\n’)
outFile.write(’Write some more.\n’)
outFile.close()
Check the contents of sample3.txt. This manner of checking the file shows it is really in the file system,
but the focus in the Tutorial should be on using Python! Run the example program printFile.py, shown
below:
inFile = open(’sample3.txt’, ’r’)
contents = inFile.read()
print(contents)
Now you have come full circle: what one Python program has written into the file sample3.txt, another has
read and displayed.
In the first line an operating system file (sample3.txt) is associated again with a Python variable name
(inFile). The second parameter again gives the mode of operation, but this time it is ’r’, short for read.
This file, sample3.txt, should already exist, and the intention is to read from it. This is the most common
mode for a file, so the ’r’ parameter is actually optional.
The read method returns all the file’s data as a single string, here assigned to contents. Using the
close method is generally optional with files being read. There is nothing to lose if a program ends without
closing a file that was being read.9
Exercise 2.5.0.2. Make the following programs in sequence. Be sure to save the programs in the same
directory as where you start the idle shortcut and where you have all the sample text files:
* a. printUpper.py: read the contents of the sample2.txt file and print the contents out in upper case. (This
should use file operations and should work no matter what the contents are in sample2.txt. Do not assume
the particular string written by nextFile.py!)
* b. fileUpper.py: prompt the user for a file name, read and print the contents of the requested file in upper
case.
9If, for some reason, you want to reread this same file while the same program is running, you need to close it and reopen
it.
2.6. SUMMARY 90
** c. copyFileUpper: modify fileUpper.py to write the upper case string to a new file rather than printing
it. Have the name of the new file be dynamically derived from the old name by prepending ’UPPER’ to
the name. For example, if the user specified the file sample.txt (from above), the program would create
a file UPPERsample.txt, containing ’MY FIRST OUTPUT FILE!’. When the user specifies the file name
stuff.txt, the resulting file would be named UPPERstuff.txt.
Exercise 2.5.0.3. Write madlib3.py, a small modification of madlib2.py, requiring only a modification
to the main function of madlib2.py. Your madlib3.py should prompt the user for the name of a file that
should contain a madlib format string as text (with no quotes around it). Read in this file and use it as the
format string in the tellStory function. This is unlike in madlib2.py, where the story is a literal string
coded directly into the program called originalStory. The tellstory function and particularly the getKeys
function were developed and described in detail in this tutorial, but for this exercise there is no need to
follow their inner workings - you are just a user of the tellstory function (and the functions that it calls).
You do not need to mess with the code for the definition of tellStory or any of the earlier supporting
functions. The original madlib string is already placed in a file jungle.txt, that is in this format as an
example. With the Idle editor, write another madlib format string into a file myMadlib.txt. If you earlier
created a file myMadlib.py, then you can easily extract the story from there (without the quotes around it).
Test your program both with jungle.txt and your new madlib story file.
2.6. Summary
The same typographical conventions will be used as in the last summary in Section 1.15.
(1) Object notation
(a) When the name of a type of object is used as a function call, it is called a constructor, and a
new object of that type is constructed and returned. The meanings of any parameters to the
constructor depend on the type. [2.2.3]
(b) object.methodName(parameters)
Objects may have special operations associated with them, called methods. They are functions
automatically applied to the object before the dot. Further parameters may be expected,
depending on the particular method. [2.1.1]
(2) String (str) indexing and methods
See the Chapter 1 Summary Section 1.15 for string literals and symbolic string operations.
(a) String Indexing. [2.1.2]
stringReference[intExpression]
Individual characters in a string may be chosen. If the string has length L, then the indices
start from 0 for the initial character and go to L-1 for the rightmost character. Negative
indices may also be used to count from the right end, -1 for the rightmost character through
-L for the leftmost character. Strings are immutable, so individual characters may be read,
but not set.
(b) String Slices [2.1.3]
stringReference[start : pastEnd]
stringReference[start : ]
stringReference[ : pastEnd]
stringReference[ : ]
A substring or slice of 0 or more consecutive characters of a string may be referred to by
specifying a starting index and the index one past the last character of the substring. If the
starting or ending index is left out Python uses 0 and the length of the string respectively.
Python assumes indices that would be beyond an end of the string actually mean the end of
the string.
(c) String Methods: Assume s refers to a string
(i) s.upper()
Returns an uppercase version of the string s. [2.1.1]
(ii) s.lower()
Returns a lowercase version of the string s.[2.1.1]
2.6. SUMMARY 91
(iii) s.count(sub)
Returns the number of repetitions of the substring sub inside s. [2.1.1]
(iv) s.find(sub)
s.find(sub, start)
s.find(sub, start, end)
Returns the index in s of the first character of the first occurrence of the substring sub
within the part of the string s indicated, respectively the whole string s, s[start : ],
or s[start : end], where start and end have integer values.[2.1.1]
(v) s.split()
s.split(sep)
The first version splits s at any sequence of whitespace (blanks, newlines, tabs) and
returns the remaining parts of s as a list. If a string sep is specified, it is the separator
that gets removed from between the parts of the list. [2.1.5]
(vi) sep.join(sequence)
Return a new string obtained by joining together the sequence of strings into one string,
interleaving the string sep between sequence elements. [2.1.6]
(vii) Further string methods are discussed in the Python Reference Manual, Section 2.3.6.1,
String Methods. [2.1.7]
(3) Sets
A set is a collection of elements with no repetitions. It can be used as a sequence in a for loop.
A set constructor can take any other sequence as a parameter, and convert the sequence to a set
(with no repetitions). Nonempty set literals are enclosed in braces. [2.2.2]
(4) List method append
aList.append(element)
Add an arbitrary element to the end of the list aList, mutating the list, not returning any list.
[2.2.1]
Many types of mutable object have ways to make a copy that is a distinct object. Zelle’s graphical
objects have the clone method. A copy of a list may be made with a full slice: someList[:]. Then
direct mutations to one list (like appending an element) do not affect the other list, but still, each
list is indirectly changed if a common mutable element in the lists is changed.
(7) Graphics
A systematic reference to Zelle’s graphics package, graphics.py, is at http://mcsp.wartburg.edu/
zelle/python/graphics/graphics/index.html.
2.6. SUMMARY 92
(a) Introductory examples of using graphics.py are in [2.4.1], [2.4.2], and [2.4.9]
(b) Windows operating system .pyw
In windows, a graphical program that take no console input and generates no console output,
may be given the extension .pyw to suppress the generation of a console window. [2.4.3]
(c) Event-driven programs
Graphical programs are typically event-driven, meaning the next operation done by the pro-
gram can be in response to a large number of possible operations, from the keyboard or mouse
for instance, without the program knowing which kind of event will come next. For simplicity,
this approach is pretty well hidden under Zelle’s graphics package, allowing the illusion of
simpler sequential programming. [2.4.4]
(d) Custom computer colors are expressed in terms of the amounts of red, green, and blue. [2.4.11]
(e) See also Animation under the summary of Programming Techniques.
(8) Additional programming techniques
(a) These techniques extend those listed in the summary of the previous chapter. [1.15]
(b) Sophisticated operations with substrings require careful setting of variables used as an index.
[2.1.4]
(c) There are a number of techniques to assist creative programming, including pseudo-code and
gradual generalization from concrete examples. [2.3.2]
(d) Animation: a loop involving small moves followed by a short delay (assumes the time module
is imported): [2.4.8
loop heading :
move all objects a small step in the proper direction
time.sleep(delay).
(e) Example of a practical successive modification loop: [2.3.1]
(f) Examples of encapsulating ideas in functions and reusing them: [2.3.1], [2.3.3], [2.4.8]
(g) Random results can be introduced into a program using the random module. [2.4.12]
CHAPTER 3
You have varied the normal forward sequence of operations with functions and for loops. To have full
power over your programs, you need two more constructions that changing the flow of control: decisions
choosing between alternatives (if statements), and more general loops that are not required to be controlled
by the elements of a collection (while loops).
3.1. If Statements
3.1.1. Simple Conditions. The statements introduced in this chapter will involve tests or conditions.
More syntax for conditions will be introduced later, but for now consider simple arithmetic comparisons that
directly translate from math into Python. Try each line separately in the Shell
2 < 5
3 > 7
x = 11
x > 10
2*x < x
type(True)
You see that conditions are either True or False (with no quotes). These are the only possible Boolean
values (named after 19th century mathematician George Boole). In Python the name Boolean is shortened
to the type bool. It is the type of the results of true-false tests.
3.1.2. Simple if Statements. Run this example program, suitcase.py. Try it at least twice, with
inputs: 30 and then 55. As you an see, you get an extra result, depending on the input. The main code is:
weight = float(input(’How many pounds does you suitcase weigh? ’))
if weight > 50:
print(’There is a $25 charge for luggage that heavy.’)
print(’Thank you for your business.’)
The middle two line are an if-statement. It reads pretty much like English. If it is true that the weight is
greater than 50, then print the statement about an extra charge. If it is not true that the weight is greater
than 50, then don’t do the indented part: skip printing the extra luggage charge. In any event, when you
have finished with the if-statement (whether it actually does anything or not), go on to the next statement
that is not indented under the if. In this case that is the statement printing “Thank you”.
The general Python syntax for a simple if statement is
if condition :
indentedStatementBlock
If the condition is true, then do the indented statements. If the condition is not true, then skip the indented
statements.
Another fragment as an example:
if balance < 0:
transfer = -balance
backupAccount = backupAccount - transfer # take enough from the backup acct.
balance = balance + transfer
As with other kinds of statements with a heading and an indented block, the block can have more than one
statement. The assumption in the example above is that if an account goes negative, it is brought back to
0 by transferring money from a backup account in several steps.
93
3.1. IF STATEMENTS 94
In the examples above the choice is between doing something (if the condition is True) or nothing (if the
condition is False). Often there is a choice of two possibilities, only one of which will be done, depending
on the truth of a condition.
3.1.3. if-else Statements. Run the example program, clothes.py. Try it at least twice, with inputs:
50, 80. As you can see, you get different results, depending on the input. The main code of clothes.py is:
temperature = float(input(’What is the temperature? ’))
if temperature > 70:
print(’Wear shorts.’)
else:
print(’Wear long pants.’)
print(’Get some exercise outside.’)
The middle four lines are an if-else statement. Again it is close to English, though you might say “oth-
erwise” instead of “else” (but else is shorter!). There are two indented blocks: One, like in the simple if
statement, comes right after the if heading and is executed when the condition in the if heading is true. In
the if-else form this is followed by an else: line, followed by another indented block that is only executed
when the original condition is false. In an if-else statement exactly one of two possible indented blocks is
executed.
A line is also shown outdented next, about getting exercise. Since it is outdented, it is not a part of the
if-else statement: It is always executed in the normal forward flow of statements, after the if-else statement
(whichever block is selected).
The general Python syntax is
if condition :
indentedStatementBlockForTrueCondition
else:
indentedStatementBlockForFalseCondition
These statement blocks can have any number of statements, and can include about any kind of statement.
3.1.4. More Conditional Expressions. All the usual arithmetic comparisons may be made, but
many do not use standard mathematical symbolism, mostly for lack of proper keys on a standard keyboard.
6 == x
6 != x
’hi’ == ’h’ + ’i’
’HI’ != ’hi’
[1, 2] != [2, 1]
An equality check does not make an assignment. Strings are case sensitive. Order matters in a list.
Try in the Shell:
’a’ > 5
When the comparison does not make sense, an Exception is caused.1
Exercise 3.1.4.1. Write a program, graduate.py, that prompts students for how many credits they
have. Print whether of not they have enough credits for graduation. (At Loyola University Chicago 128
credits are needed for graduation.)
Exercise 3.1.4.2. Following up on the discussion of the inexactness of float arithmetic in Section 1.14.3,
confirm that Python does not consider .1 + .2 to be equal to .3: Write a simple condition into the Shell to
test.
Here is another example: Pay with Overtime. Given a person’s work hours for the week and regular
hourly wage, calculate the total pay for the week, taking into account overtime. Hours worked over 40 are
overtime, paid at 1.5 times the normal rate. This is a natural place for a function enclosing the calculation.
Read the setup for the function:
def calcWeeklyWages(totalHours, hourlyWage):
’’’Return the total weekly wages for a worker working totalHours,
with a given regular hourlyWage. Include overtime for hours over 40.
’’’
The problem clearly indicates two cases: when no more than forty hours are worked or when more than
40 hours are worked. In case more than 40 hours are worked, it is convenient to introduce a variable
overtimeHours. You are encouraged to think about a solution before going on and examining mine.
You can try running my complete example program, wages.py, also shown below. The format operation
at the end of the main function uses the floating point format (Section 1.14.3) to show two decimal places
for the cents in the answer:
def calcWeeklyWages(totalHours, hourlyWage):
’’’Return the total weekly wages for a worker working totalHours,
with a given regular hourlyWage. Include overtime for hours over 40.
’’’
def main():
hours = float(input(’Enter hours worked: ’))
wage = float(input(’Enter dollars paid per hour: ’))
total = calcWeeklyWages(hours, wage)
print(’Wages are ${total:.2f}.’.format(**locals()))
main()
Here the input was intended to be numeric, but it could be decimal so the conversion from string was via
float, not int.
1This is an improvement that is new in Python 3.0
3.1. IF STATEMENTS 96
Below is an equivalent alternative version of the body of calcWeeklyWages, used in wages1.py. It uses
just one general calculation formula and sets the parameters for the formula in the if statement. There are
generally a number of ways you might solve the same problem!
if totalHours <= 40:
regularHours = totalHours
overtime = 0
else:
overtime = totalHours - 40
regularHours = 40
return hourlyWage*regularHours + (1.5*hourlyWage)*overtime
3.1.5. Multiple Tests and if-elif Statements . Often you want to distinguish between more than
two distinct cases, but conditions only have two possible results, True or False, so the only direct choice is
between two options. As anyone who has played “20 Questions” knows, you can distinguish more cases by
further questions. If there are more than two choices, a single test may only reduce the possibilities, but
further tests can reduce the possibilities further and further. Since most any kind of statement can be placed
in an indented statement block, one choice is a further if statement. For instance consider a function to
convert a numerical grade to a letter grade, ’A’, ’B’, ’C’, ’D’ or ’F’, where the cutoffs for ’A’, ’B’, ’C’, and
’D’ are 90, 80, 70, and 60 respectively. One way to write the function would be test for one grade at a time,
and resolve all the remaining possibilities inside the next else clause:
def letterGrade(score):
if score >= 90:
letter = ’A’
else: # grade must be B, C, D or F
if score >= 80:
letter = ’B’
else: # grade must be C, D or F
if score >= 70:
letter = ’C’
else: # grade must D or F
if score >= 60:
letter = ’D’
else:
letter = ’F’
return letter
This repeatedly increasing indentation with an if statement as the else block can be annoying and dis-
tracting. A preferred alternative in this situation, that avoids all this indentation, is to combine each else
and if block into an elif block:
def letterGrade(score):
if score >= 90:
letter = ’A’
elif score >= 80:
letter = ’B’
elif score >= 70:
letter = ’C’
elif score >= 60:
letter = ’D’
else:
letter = ’F’
return letter
The most elaborate syntax for an if statement, if-elif-...-else is indicated in general below:
if condition1 :
3.1. IF STATEMENTS 97
indentedStatementBlockForTrueCondition1
elif condition2 :
indentedStatementBlockForFirstTrueCondition2
elif condition3 :
indentedStatementBlockForFirstTrueCondition3
elif condition4 :
indentedStatementBlockForFirstTrueCondition4
else:
indentedStatementBlockForEachConditionFalse
The if, each elif, and the final else line are all aligned. There can be any number of elif lines, each
followed by an indented block. (Three happen to be illustrated above.) With this construction exactly one
of the indented blocks is executed. It is the one corresponding to the first True condition, or, if all conditions
are False, it is the block after the final else line.
Be careful of the strange Python contraction. It is elif, not elseif. A program testing the letterGrade
function is in example program grade1.py.
Exercise 3.1.5.1. In Idle, load grade1.py and save it as grade2.py Modify grade2.py so it has an
equivalent version of the letterGrade function that tests in the opposite order, first for F, then D, C, ....
Hint: How many tests do you need to do? 2 Be sure to run your new version and test with different inputs
that test all the different paths through the program.
Exercise 3.1.5.2. Write a program sign.py to ask the user for a number. Print out which category
the number is in: ’positive’, ’negative’, or ’zero’.
Exercise 3.1.5.3. Modify the wages.py or the wages1.py example to create a program wages2.py that
assumes people are paid double time for hours over 60. Hence they get paid for at most 20 hours overtime
at 1.5 times the normal rate. For example, a person working 65 hours with a regular wage of $10 per hour
would work at $10 per hour for 40 hours, at 1.5*$10 for 20 hours of overtime, and 2*$10 for 5 hours of double
time, for a total of 10*40 + (1.5*10)*20 + (2*10)*5 = $800. You may find wages1.py easier to adapt than
wages.py.
A final alternative for if statements: if-elif-.... with no else. This would mean changing the syntax
for if-elif-else above so the final else: and the block after it would be omitted. It is similar to the basic if
statement without an else, in that it is possible for no indented block to be executed. This happens if none
of the conditions in the tests are true.
With an else included, exactly one of the indented blocks is executed. Without an else, at most one
of the indented blocks is executed.
if weight > 120:
print(’Sorry, we can not take a suitcase that heavy.’)
elif weight > 50:
print(’There is a $25 charge for luggage that heavy.’)
This if-elif statement only prints a line if there is a problem with the weight of the suitcase.
3.1.6. Nesting Control-Flow Statements, Part I. The power of a language like Python comes
largely from the variety of ways basic statements can be combined. In particular, for and if statements
can be nested inside each other’s indented blocks. For example, suppose you want to print only the positive
numbers from an arbitrary list of numbers in a function with the following heading. Read the pieces for now.
def printAllPositive(numberList):
’’’Print only the positive numbers in numberList.’’’
For example, suppose numberList is [3, -5, 2, -1, 0, 7]. As a human, who has eyes of amazing capacity,
you are drawn immediately to the actual correct numbers, 3, 2, and 7, but clearly a computer doing this
systematically will have to check every number. That easily suggests a for-each loop starting
for num in numberList:
24 tests to distinguish the 5 cases, as in the previous version
3.1. IF STATEMENTS 98
What happens in the body of the loop is not the same thing each time: some get printed, and for those we
will want the statement
print(num)
but some do not get printed, so it may at first seem that this is not an appropriate situation for a for-each
loop, but in fact, there is a consistent action required: Every number must be tested to see if it should be
printed. This suggests an if statement, with the condition num > 0. Try loading into Idle and running the
example program onlyPositive.py, whose code is shown below. It ends with a line testing the function:
def printAllPositive(numberList):
’’’Print only the positive numbers in numberList.’’’
for num in numberList:
if num > 0:
print(num)
dx = -dx
if y < yLow:
dy = -dy
if y > yHigh
dy = -dy
This approach would cause there to be some extra testing: If it is true that x < xLow, then it is impossible
for it to be true that x > xHigh, so we do not need both tests together. We avoid unnecessary tests with
an elif clause (for both x and y):
if x < xLow:
dx = -dx
elif x > xHigh
dx = -dx
if y < yLow:
dy = -dy
elif y > yHigh
dy = -dy
Note that the middle if is not changed to an elif, because it is possible for the ball to reach a corner, and
need both dx and dy reversed.
The program also uses several accessor methods for graphics objects that we have not used in examples
yet. Various graphics objects, like the circle we are using as the shape, know their center point, and it can
be accessed with the getCenter() method. (Actually a clone of the point is returned.) Also each coordinate
of a Point can be accessed with the getX() and getY() methods.
This explains the new features in the central function defined for bouncing around in a box, bounceInBox.
The animation arbitrarily goes on in a simple repeat loop for 600 steps. (A later example will improve this
behavior.):
def bounceInBox(shape, dx, dy, xLow, xHigh, yLow, yHigh):
’’’ Animate a shape moving in jumps (dx, dy), bouncing when
its center reaches the low and high x and y coordinates.
’’’
delay = .005
for i in range(600):
shape.move(dx, dy)
center = shape.getCenter()
x = center.getX()
y = center.getY()
if x < xLow:
dx = -dx
elif x > xHigh:
dx = -dx
if y < yLow:
dy = -dy
elif y > yHigh:
dy = -dy
time.sleep(delay)
The program starts the ball from an arbitrary point inside the allowable rectangular bounds. This is
encapsulated in a utility function included in the program, getRandomPoint. The getRandomPoint function
uses the randrange function from the module random. Note that in parameters for both the functions range
and randrange, the end stated is past the last value actually desired:
def getRandomPoint(xLow, xHigh, yLow, yHigh):
’’’Return a random Point with coordinates in the range specified.’’’
x = random.randrange(xLow, xHigh+1)
3.1. IF STATEMENTS 100
y = random.randrange(yLow, yHigh+1)
return Point(x, y)
The full program is listed below, repeating bounceInBox and getRandomPoint for completeness. Several
parts that may be useful later, or are easiest to follow as a unit, are separated out as functions. Make sure
you see how it all hangs together or ask questions!
’’’
Show a ball bouncing off the sides of the window.
’’’
from graphics import *
import time, random
delay = .005
for i in range(600):
shape.move(dx, dy)
center = shape.getCenter()
x = center.getX()
y = center.getY()
if x < xLow:
dx = -dx
elif x > xHigh:
dx = -dx
if y < yLow:
dy = -dy
elif y > yHigh:
dy = -dy
time.sleep(delay)
winWidth = 290
winHeight = 290
3.1. IF STATEMENTS 101
radius = 10
xLow = radius # center is separated from the wall by the radius at a bounce
xHigh = winWidth - radius
yLow = radius
yHigh = winHeight - radius
bounceBall(3, 5)
3.1.7. Compound Boolean Expressions. To be eligible to graduate from Loyola University Chicago,
you must have 128 units of credit and a GPA of at least 2.0. This translates directly into Python as a
compound condition:
units >= 128 and GPA >=2.0
This is true if both units >= 128 is true and GPA >=2.0 is true. A short example program using this would
be:
units = input(’How many units of credit do you have? ’)
GPA = input(’What is your GPA? ’)
if units >= 128 and GPA >=2.0:
print(’You are eligible to graduate!’)
else:
print(’You are not eligible to graduate.’)
The new Python syntax is for the operator and:
condition1 and condition2
It is true if both of the conditions are true. It is false if at least one of the conditions is false.
Exercise 3.1.7.1. A person is eligible to be a US Senator who is at least 30 years old and has been
a US citizen for at least 9 years. Write a version of a program congress.py to obtain age and length of
citizenship from the user and print out if a person is eligible to be a Senator or not. A person is eligible to
be a US Representative who is at least 25 years old and has been a US citizen for at least 7 years. Elaborate
your program congress.py so it obtains age and length of citizenship and prints whether a person is eligible
to be a US Representative only, or is eligible for both offices, or is eligible for neither.
In the last example in the previous section, there was an if-elif statement where both tests had the same
block to be done if the condition was true:
if x < xLow:
dx = -dx
elif x > xHigh:
dx = -dx
There is a simpler way to state this in a sentence: If x < xLow or x > xHigh, switch the sign of dx. That
translates directly into Python:
if x < xLow or x > xHigh:
dx = -dx
The word or makes another compound condition:
condition1 or condition2
3.1. IF STATEMENTS 102
is true if at least one of the conditions is true. It is false if both conditions are false. This corresponds to one
way the word “or” is used in English. Other times in English “or” is used to mean exactly one alternative is
true.
It is often convenient to encapsulate complicated tests inside a function. Think how to complete the
function starting:
def isInside(rect, point):
’’’Return True if the point is inside the Rectangle rect.’’’
pt1 = rect.getP1()
pt2 = rect.getP2()
Recall that a Rectangle is specified in its constructor by two diagonally oppose Points. This example gives
the first use in the tutorials of the Rectangle methods that recover those two corner points, getP1 and
getP2. The program calls the points obtained this way p1 and p2. The x and y coordinates of pt1, pt2,
and point can be recovered with the methods of the Point type, getX() and getY().
Suppose that I introduce variables for the x coordinates of p1, point, and p2, calling these x-coordinates
end1, val, and end2, respectively. On first try you might decide that the needed mathematical relationship
to test is
end1 <= val <= end2
Unfortunately, this is not enough: The only requirement for the two corner points is that they be diagonally
opposite, not that the coordinates of the second point are higher than the corresponding coordinates of the
first point. It could be that end1 is 200; end2 is 100, and val is 120. In this latter case val is between end1
and end2, but substituting into the expression above
200 <= 120 <= 100
is False. The 100 and 200 need to be reversed in this case. This makes a complicated situation, and an issue
which must be revisited for both the x and y coordinates. I introduce an auxiliary function isBetween to
deal with one coordinate at a time. It starts:
def isBetween(val, end1, end2):
’’’Return True if val is between the ends.
The ends do not need to be in increasing order.’’’
Clearly this is true if the original expression, end1 <= val <= end2, is true. You must also consider the
possible case when the order of the ends is reversed: end2 <= val <= end1. How do we combine these two
possibilities? The Boolean connectives to consider are and and or. Which applies? You only need one to be
true, so or is the proper connective:
A correct but redundant function body would be:
if end1 <= val <= end2 or end2 <= val <= end1:
return True
else:
return False
Check the meaning: if the compound expression is True, return True. If the condition is False, return False
– in either case return the same value as the test condition. See that a much simpler and neater version is
to just return the value of the condition itself!
return end1 <= val <= end2 or end2 <= val <= end1
In general you should not need an if-else statement to choose between true and false values!
A side comment on expressions like
end1 <= val <= end2
Other than the two-character operators, this is like standard math syntax, chaining comparisons. In Python
any number of comparisons can be chained in this way, closely approximating mathematical notation.
Though this is good Python, be aware that if you try other high-level languages like Java and C++, such
an expression is gibberish. Another way the expression can be expressed (and which translates directly to
other languages) is:
end1 <= val and val <= end2
3.1. IF STATEMENTS 103
So much for the auxiliary function isBetween. Back to the isInside function. You can use the isBetween
function to check the x coordinates, isBetween(point.getX(), p1.getX(), p2.getX()), and to check the
y coordinates, isBetween(point.getY(), p1.getY(), p2.getY()). Again the question arises: how do
you combine the two tests?
In this case we need the point to be both between the sides and between the top and bottom, so the
proper connector is and.
Think how to finish the isInside method. Hint: 3
Sometimes you want to test the opposite of a condition. As in English you can use the word not. For
instance, to test if a Point was not inside Rectangle Rect, you could use the condition
not isInside(rect, point)
In general,
not condition
is True when condition is False, and False when condition is True.
The example program chooseButton1.py, shown below, is a complete program using the isInside func-
tion in a simple application, choosing colors. Pardon the length. Do check it out. It will be the starting
point for a number of improvements that shorten it and make it more powerful in the next section. First a
brief overview:
The program includes the functions isBetween and isInside that have already been discussed. The
program creates a number of colored rectangles to use as buttons and also as picture components. Aside
from specific data values, the code to create each rectangle is the same, so the action is encapsulated in a
function, makeColoredRect. All of this is fine, and will be preserved in later versions.
The present main function is long, though. It has the usual graphics starting code, draws buttons and
picture elements, and then has a number of code sections prompting the user to choose a color for a picture
element. Each code section has a long if-elif-else test to see which button was clicked, and sets the color of
the picture element appropriately.
’’’Make a choice of colors via mouse clicks in Rectangles --
A demonstration of Boolean operators and Boolean functions.’’’
pt1 = rect.getP1()
pt2 = rect.getP2()
return isBetween(point.getX(), pt1.getX(), pt2.getX()) and \
isBetween(point.getY(), pt1.getY(), pt2.getY())
corner2 = corner.clone()
corner2.move(width, -height)
rect = Rectangle(corner, corner2)
rect.setFill(color)
3Once again, you are calculating and returning a Boolean result. You do not need an if-else statement.
3.1. IF STATEMENTS 104
rect.draw(win)
return rect
def main():
winWidth = 400
winHeight = 400
win = GraphWin(’pick Colors’, winWidth, winHeight)
win.setCoords(0, 0, winWidth, winHeight)
main()
The only further new feature used is in the long return statement in isInside.
Recall that Python is smart enough to realize that a statement continues to the next line if there is an
unmatched pair of parentheses or brackets. Above is another situation with a long statement, but there are
no unmatched parentheses on a line. For readability it is best not to make an enormous long line that would
run off your screen or paper. Continuing to the next line is recommended. You can make the final character
on a line be a backslash (\) to indicate the statement continues on the next line. This is not particularly
neat, but it is a rather rare situation. Most statements fit neatly on one line, and the creator of Python
decided it was best to make the syntax simple in the most common situation. (Many other languages require
a special statement terminator symbol like ’;’ and pay no attention to newlines).
The chooseButton1.py program is long partly because of repeated code. The next section gives another
version involving lists.
(rectangle, choice), return the choice that goes with the rectangle
in win where the mouse gets clicked, or return default if the click
is in none of the rectangles.’’’
point = win.getMouse()
for (rectangle, choice) in choicePairs:
#....
This is the first time we have had a for-loop going through a list of tuples. Recall that we can do multiple
assignments at once with tuples. This also works in a for-loop heading. The for-loop goes through one
tuple in the list choicePairs at a time. The first time through the loop the tuple taken from the list
is (redButtton, ’red’). This for-loop does a multiple assignment to (rectangle, choice)each time
through the loop, so the first time rectangle refers to redButton and choice refers to ’red’. The next
time through the loop, the second tuple from the list is used, (yellowButton, ’yellow’)so this time inside
the loop rectangle will refer to yellowButton and choice refers to ’yellow’....This is a neat Python
feature.4
There is still a problem. We could test each rectangle in the for-each loop, but the original if-elif...
statement in chooseButton1.py stops when the first condition is true. For-each statements are designed to go
all the way through the sequence. There is a simple way out of this in a function: A return statement always
stops the execution of a function. When we have found the rectangle containing the point, the function can
return the desired choice immediately!
def getChoice(choicePairs, default, win):
’’’Given a list of tuples (rectangle, choice), return the choice
that goes with the rectangle in win where the mouse gets clicked,
or return default if the click is in none of the rectangles.’’’
point = win.getMouse()
for (rectangle, choice) in choicePairs:
if isInside(point, rectangle):
return choice
return default
Note that the else part in chooseButton1.py corresponds to the statement after the loop above. If execution
gets past the loop, then none of the conditions tested in the loop was true.
With appropriate parameters, the looping function is a complete replacement for the original if-elif
statement! The replacement has further advantages.
• There can be an arbitrarily long list of pairs, and the exact same code works.
• This code is clearer and easier to read, since there is no need to read through a long sequence of
similar if-elif clauses.
• The names of the rectangles in the tuples in the list are never referred to. They are unnecessary
here. Only a list needs to be specified. That could be useful earlier in the program ....
Are individual names for the rectangles needed earlier? No, the program only need to end up with the pairs
of the form (rectangle, color) in a list. The statements in the original program, below, have a similar form
which will allow them to be rewritten:
redButton = makeColoredRect(Point(310, 350), 80, 30, ’red’, win)
yellowButton = makeColoredRect(Point(310, 310), 80, 30, ’yellow’, win)
blueButton = makeColoredRect(Point(310, 270), 80, 30, ’blue’, win)
As stated earlier, we could use the statements above and then make a list of pairs with the statement
4Particularly in other object-oriented languages where lists and tuples are way less easy to use, the preferred way to group
associated objects, like rectangle and choice, is to make a custom object type containing them all. This is also possible and
often useful in Python. In some relatively simple cases, like in the current example, use of tuples can be easier to follow, though
the approach taken is a matter of taste. The topic of creating custom type of objects will not be taken up in these tutorials.
3.2. LOOPS AND TUPLES 107
Now I will look at an alternative that would be particularly useful if there were considerably more buttons
and colors.
All the assignment statements with makeColorRect have the same format, but differing data for several
parameters. I use that fact in the alternate code:
choicePairs = list()
buttonSetup = [(310, 350, ’red’), (310, 310, ’yellow’), (310, 270, ’blue’)]
for (x, y, color) in buttonSetup:
button = makeColoredRect(Point(x, y), 80, 30, color, win)
choicePairs.append((button, color))
I extract the changing data from the creation of the rectangles into a list, buttonSetup. Since more than one
data items are different for each of the original lines, the list contains a tuple of data from each of the original
lines. Then I loop through this list and not only create the rectangles for each color, but also accumulates
the (rectangle, color) pairs for the list choicePairs.
Note the double parentheses in the last line of the code. The outer ones are for the method call. The
inner ones create a single tuple as the parameter.
Assuming I do not need the original individual names of the Rectangles, this code with the loop will
completely substitute for the previous code with its separate lines with the separate named variables and
the recurring formats.
This code has advantages similar to those listed above for the getChoice code.
Now look at what this new code means for the interactive part of the program. The interactive code
directly reduces to
msg = Text(Point(winWidth/2, 375),’Click to choose a house color.’)
msg.draw(win)
color = getChoice(colorPairs, ’white’, win)
house.setFill(color)
Can you finish the body of the loop? Look at the original version of the interactive code. When you are
done thinking about it, go on to my solution. The entire code is in example program chooseButton2.py,
and also below. The changes from chooseButton1.py are in three blocks, each labeled #NEW in the code.
The new parts are the getChoice function and the two new sections of main with the loops:
’’’Make a choice of colors via mouse clicks in Rectangles --
Demonstrate loops using lists of tuples of data.’’’
pt1 = rect.getP1()
pt2 = rect.getP2()
return isBetween(point.getX(), pt1.getX(), pt2.getX()) and \
isBetween(point.getY(), pt1.getY(), pt2.getY())
corner2 = corner.clone()
corner2.move(width, -height)
rect = Rectangle(corner, corner2)
rect.setFill(color)
rect.draw(win)
return rect
point = win.getMouse()
for (rectangle, choice) in choicePairs:
if isInside(point, rectangle):
return choice
return default
def main():
winWidth = 400
winHeight = 400
win = GraphWin(’Pick Colors’, winWidth, winHeight)
win.setCoords(0, 0, winWidth, winHeight)
main()
Run it.
With the limited number of choices in chooseButton1.py, the change in length to convert to chooseBut-
ton2.py is not significant, but the change in organization is significant if you try to extend the program, as
in the exercise below. See if you agree!
Exercise 3.2.0.2. a. Write a program chooseButton3.py, modifying chooseButton2.py. Look at the
format of the list buttonSetup, and extend it so there is a larger choice of buttons and colors. Add at least
one button and color.
b. Further extend the program chooseButton3.py by adding some further graphical object shape to
the picture, and extend the list shapePairs, so they can all be interactively colored.
c. (Optional) If you would like to carry this further, also add a prompt to change the outline color of
each shape, and then carry out the changes the user desires.
d. (Optional Challenge) Look at the pattern within the list buttonSetup. It has a consistent x coordi-
nate, and there is a regular pattern to the change in the y coordinate (a consistent decrease each time). The
only data that is arbitrary each time is the sequence of colors. Write a further version chooseButton4.py
with a function makeButtonSetup, that takes a list of color names as a parameter and uses a loop to create
the list used as buttonSetup. End by returning this list. Use the function to initialize buttonSetup. If you
like, make the function more general and include parameters for the x coordinate, the starting y coordinate
and the regular y coordinate change.
while condition :
indentedBlock
Setting up the English example in a similar format would be:
while your tea is too hot :
add a chip of ice
To make things concrete and numerical, suppose the following: The tea starts at 115 degrees Fahrenheit.
You want it at 112 degrees. A chip of ice turns out to lower the temperature one degree each time. You test
the temperature each time, and also print out the temperature before reducing the temperature. In Python
you could write and run the code below, saved in example program cool.py:
temperature = 115 #1
while temperature > 112: #2
print(temperature) #3
temperature = temperature - 1 #4
print(’The tea is cool enough.’) #5
I added a final line after the while loop to remind you that execution follows sequentially after a loop
completes.
If you play computer and follow the path of execution, you could generate the following table. Remem-
ber, that each time you reach the end of the indented block after the while heading, execution returns to
the while heading:
not in the middle of the loop. Predict what will happen with this slight variation on the previous example,
switching the order in the loop body. Follow it carefully, one step at a time.
i = 4 #1
while (i < 9): #2
i = i+2 #3
print(i) #4
Check yourself by running the example program testWhile2.py.
The sequence order is important. The variable i is increased before it is printed, so the first number
printed is 6. Another common error is to assume that 10 will not be printed, since 10 is past 9, but the
test that may stop the loop is not made in the middle of the loop. Once the body of the loop is started, it
continues to the end, even when i becomes 10.
line i comment
1 4
2 4 < 9 is true, do loop
3 6 4+2=6
4 print 6
2 6 < 9 is true, do loop
3 8 6+2= 8
4 print 8
2 8 < 9 is true, do loop
3 10 8+2=10
4 112 print 10
2 10 < 9 is false, skip loop
Predict what happens in this related little program:
nums = list()
i = 4
while (i < 9):
nums.append(i)
i = i+2
print(nums)
Check yourself by running the example program testWhile3.py.
3.3.2. The range Function, In General. There is actually a much simpler way to generate the
previous sequence, using a further variation of the range function. Enter these lines separately in the Shell.
As in the simpler applications of range, the values are only generated one at a time, as needed. To see the
entire sequence at once, convert the sequence to a list:
nums = list(range(4, 9, 2))
print(nums)
The third parameter is needed when the step size from one element to the next is not 1.
The most general syntax is
range(start, pastEnd, step)
The value of the second parameter is always past the final element of the list. Each element after the first
in the list is step more than the previous one. Predict and try in the Shell:
list(range(4, 10, 2))
Actually the range function is even more sophisticated than indicated by the while loop above. The step
size can be negative.
Try in the Shell:
list(range(10, 0, -1))
Do you see how 0 is past the end of the list?
3.3. WHILE STATEMENTS 112
Make up a range function call to generate the list of temperatures printed in the tea example, 115,
114, 113. Test it in the Shell.
3.3.3. Interactive while Loops. The earlier examples of while loops were chosen for their simplicity.
Obviously they could have been rewritten with range function calls. Now lets try a more interesting example.
Suppose you want to let a user enter a sequence of lines of text, and want to remember each line in a list.
This could easily be done with a simple repeat loop if you knew the number of lines to enter. For example
if you want three lines:
lines = list()
print(’Enter 3 lines of text’)
for i in range(3):
line = input(’Next line: ’)
lines.append(line)