Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
13 views

Hands-On Python Tutorial

Uploaded by

EMMANUEL JOHNSON
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views

Hands-On Python Tutorial

Uploaded by

EMMANUEL JOHNSON
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 355

Loyola University Chicago

Loyola eCommons
Computer Science: Faculty Publications and Other
Faculty Publications
Works

2015

Hands-on Python Tutorial


Andrew N. Harrington
Loyola University Chicago, aharrin@luc.edu

Recommended Citation
Harrington, A. 2015. Hands-on Python Tutorial. http://anh.cs.luc.edu/python/hands-on/3.1/handsonHtml/.

This Other is brought to you for free and open access by the Faculty Publications at Loyola eCommons. It has been accepted for inclusion in Computer
Science: Faculty Publications and Other Works by an authorized administrator of Loyola eCommons. For more information, please contact
ecommons@luc.edu.
Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License.
© Andrew Harrington, 2015.
Hands-on Python Tutorial » next | index

Next topic Hands-on Python Tutorial


1. Beginning With Python
Python 3.1 Version
This Page
Show Source Dr. Andrew N. Harrington

Quick search Computer Science Department, Loyola University Chicago

© Released under the Creative Commons Attribution-Noncommercial-


Go Share Alike 3.0 United States License
http://creativecommons.org/licenses/by-nc-sa/3.0/us/
Enter search terms or a module,
class or function name.

1. Beginning With Python


1.1. Context
1.2. The Python Interpreter and Idle, Part I
1.3. Whirlwind Introduction To Types and Functions
1.4. Integer Arithmetic
1.5. Strings, Part I
1.6. Variables and Assignment
1.7. Print Function, Part I
1.8. Strings Part II
1.9. The Idle Editor and Execution
1.10. Input and Output
1.11. Defining Functions of your Own
1.12. Dictionaries
1.13. Loops and Sequences
1.14. Decimals, Floats, and Floating Point Arithmetic
1.15. Summary
2. Objects and Methods
2.1. Strings, Part III
2.2. More Classes and Methods
2.3. Mad Libs Revisited
« 2.4. Graphics
2.5. Files
2.6. Summary
3. More On Flow of Control
3.1. If Statements
3.2. Loops and Tuples
3.3. While Statements
3.4. Arbitrary Types Treated As Boolean
3.5. Further Topics to Consider
3.6. Summary
4. Dynamic Web Pages
4.1. Overview
4.2. Web page Basics
4.3. Composing Web Pages in Python
4.4. CGI - Dynamic Web Pages
4.5. Summary

Index

Hands-on Python Tutorial » next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Sep 08, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » index

Quick search Index


Go Symbols | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | R | S |
T|U|V|W|Y|Z
Enter search terms or a module,
class or function name.
Symbols
!= //
" :
""" end of heading
#! list slice
% string slice
' <
''' <...> web page tags
* <=
important tutorial problem ==
multiplication of numbers >
string repeat >=
** [:]
challenging tutorial problem list slice
dictionary string slice
exponent []
square root list indexing
+ string indexing
addition \
concatenation escape code
- line continuation
negation \n newline
subtraction {}
. format string
method precedence set
method syntax
/

A
abstraction animation
accumulation loop checkMouse
actual parameter graphics
Alt-2 Idle window resize sleep
and while
non-boolean operands append method for list
approximation
assignment statement
autocompletion in Idle
B
boolean braces
comparison operators format string
interpretation as set
operations in + not in break
boolean operation
and
not
or

C
camel case concrete case
case sensitive constant
cgi constructor
errors consumer of a function
module context
chaining comparisons continue
checkMouse conversion - int and str
animation count string method
class
color_rgb
comparison
chaining
operators
concatenation +

D
def division / // % remainder
dictionary division of floats
definition documentation string
format string function
variable program
dir for method list

E
editor example program
Idle interview
end keyword parameter mad lib
endswith string method exception
error execute
execution function
syntax execution
traceback Idle edit window
escape codes \\ \n error
event error traceback
for loop sequence
function;
indentation
sequence with for loop
exercise
madlib while
exponent
**

F
file for-each
Python module loop
read multiple assignment with tuples
write formal parameter
find string method format
float float
approximation string and method
division format string
format **dictionary
precision precision
round function
type consumer or writer
flow of control definition
folder for Python examples execute
for execution sequence
accumulation loop help
execution sequence, [1] input with prompt
simple repeat loop parameter, [1]
statement range, one parameter
range, two parameter
return

G
global constant graphics methods added
graphics graphics object string
Zelle conversion
Zelle's documentation promptClose
animation yUp
order of drawing guiding tutorial principals

H
Hello world high level language
running a program
help function

I
identifier index
naming conventions string [ ]
Idle variable
Alt-2 resize inheritance
autocompletion input function with prompt
bug running a Python 3.2 int
program conversion
copy line in shell digit string
execution of editor file integer
loading a program interactive while loop
running a program interview example program
shell (and see shell in the
index)
starting
windows
if statement
if-elif
if-else
in boolean operator on sequence,
[1]
indentation

J
join string method for list

K
keyword parameter Kompozer
end
sep

L
language - high level and machine loading a program in Idle
len function local
line continuation \ scope
list localhost
[ ] index + [ : ] slice web page
append loop
from string split accumulation
join strings for statement
type for-each
list indexing interactive
literal outline
repeat - simple
return from inside
split
successive modification
while
« lower string method

M
Mac module
Alt replacement in Idle multiple assignment
Idle Save As for with tuples
machine language tuple
mad lib multiple word identifier
first example program multiplication
madlib numbers
while exercise string
method mutable object
. syntax
dir
object
string format

N
naming conventions None
nested control statements absence of data
newline return
\n not
print not in

O
object or
method non-boolean operands
mutable order of drawing
string
object orientation

P
parameter problem solving, [1]
actual and formal prompt in input function
variable length list promptClose
playing computer, [1] Python
function returning a value downloading and installing
precedence web page
. for method why
arithmetic pyw
precision
float
format string
print
end keyword parameter
sep keyword parameter

R
random.randrange replace string method
range reserved word
one parameter return
three parameter None
two parameter from inside loop
read a file without a value
recursion RGB color creation - color_rgb
remainder round a float
repeat loop - simple running a program
Hello world
Idle

S
scope split
global loop
local string method
screen layout and Alt-2 resize while
sep square root **
sequence startswith string method
for loop, [1] statement
function assignment
in as boolean operator for loop
len function if
set return
set while
shell str
Mac Save As conversion
Mac default folder type for string
confusion with Edit Window string
copy line concatenation
introduction count
names remembered after delimiters
execution digits
program input empty
running a program escape codes \\ \n
sequence in doc string find method
simple repeat loop index
sleep join method
slice [ : ] lower
list multiplication
string object
slice [ : ]
split method
startswith + endswith + replace
str type
triple quote
upper
successive modification loop
syntax error
syntax template typography

T
tags in web pages tuple
time.sleep for-each multiple assignment
traceback multiple assignment
triple quoted string type
float
function
int
list
str
typography for syntax

U
underscores in identifiers upper string method

V
variable
assignment
dictionary
update

W
web page write a file
Kompozer writer of a function
Python
basics
document naming conventions
dynamic
execution sequence
form
localhost
static
tags
webbrowser module
while
animation
interactive
loop
split
statement

Y
yUp

Z
Zelle zip file - can't edit
graphics
graphics documentation
Hands-on Python Tutorial » index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Sep 08, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » previous | next | index

Previous topic 1. Beginning With Python


Hands-on Python Tutorial
© Released under the Creative commons Attribution-Noncommercial-
Next topic Share Alike 3.0 United States License
1.1. Context http://creativecommons.org/licenses/by-nc-sa/3.0/us/
This Page
1.1. Context
Show Source 1.1.1. Low-Level and High-Level Computer Operations
1.1.2. Why Python
Quick search
1.1.3. Obtaining Python for Your Computer
1.1.4. Philosophy and Implementation of the Hands-On
Go Python Tutorials
1.1.5. Using the Tutorial - Text and Video
Enter search terms or a module, 1.1.6. Learning to Problem-Solve
class or function name.
1.2. The Python Interpreter and Idle, Part I
1.2.1. Your Python Folder and Python Examples
1.2.2. Running A Sample Program
1.2.3. A Sample Program, Explained
1.2.4. Starting Idle
1.2.5. Windows in Idle
1.3. Whirlwind Introduction To Types and Functions
1.4. Integer Arithmetic
1.4.1. Addition and Subtraction
1.4.2. Multiplication, Parentheses, and Precedence
1.4.3. Division and Remainders
1.5. Strings, Part I
1.5.1. String Delimiters, Part I
1.5.2. String Concatenation
1.5.2.1. String Exercise
1.6. Variables and Assignment
1.6.1. Literals and Identifiers
1.7. Print Function, Part I
1.8. Strings Part II
1.8.1. Triple Quoted String Literals
1.8.2. Escape Codes
1.9. The Idle Editor and Execution
1.9.1. Loading a Program in the Idle Editor, and Running It
1.9.2. A Pre Python 3.3 Idle Bug
1.9.3. The Classic First Program
1.9.4. Program Documentation String
1.9.5. Screen Layout
1.10. Input and Output
1.10.1. The input Function
1.10.2. Print with Keyword Parameter sep
1.10.3. Numbers and Strings of Digits
1.10.3.1. Exercise for Addition
1.10.3.2. Exercise for Quotients
1.10.4. String Format Operation
1.10.4.1. Addition Format Exercise
1.10.4.2. Quotient Format Exercise
1.11. Defining Functions of your Own
1.11.1. Syntax Template Typography
1.11.2. A First Function Definition
1.11.3. Multiple Function Definitions
1.11.3.1. Poem Function Exercise
1.11.4. Function Parameters
1.11.4.1. Birthday Function Exercise
1.11.5. Multiple Function Parameters
1.11.5.1. Quotient Function Exercise
1.11.6. Returned Function Values
1.11.6.1. Quotient String Return Exercise
1.11.7. Two Roles: Writer and Consumer of Functions
1.11.8. Local Scope
1.11.9. Global Constants
1.12. Dictionaries
1.12.1. Definition and Use of Dictionaries
1.12.1.1. Number Dictionary Exercise
1.12.2. Dictionaries and String Formatting
1.12.2.1. Mad Lib Exercise
1.12.3. Dictionaries and Python Variables
« 1.12.3.1. Quotient String Dictionary Exercise
1.13. Loops and Sequences
1.13.1. Updating Variables
1.13.2. The list Type
1.13.3. The range Function, Part 1
1.13.4. Basic for Loops
1.13.4.1. Pattern Loop Exercise
1.13.4.2. Triple Exercise
1.13.5. Simple Repeat Loop
1.13.6. Successive Modification Loops
1.13.7. Accumulation Loops
1.13.7.1. Play Computer sumList Exercise
1.13.7.2. Test sumList Exercise
1.13.7.3. Join All Exercise
1.13.8. More Playing Computer
1.13.8.1. Play Computer Odd Loop Exercise
1.13.8.2. Play Computer Error Exercise
1.13.8.3. Play Computer Functions Exercise
1.13.9. The print function keyword end
1.14. Decimals, Floats, and Floating Point Arithmetic
1.14.1. Floats, Division, Mixed Types
1.14.2. Exponentiation, Square Roots
1.14.3. String Formats for Float Precision
1.14.3.1. Floating Point Exercise
1.15. Summary

Hands-on Python Tutorial » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 25, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Table Of Contents 1.1. Context


1.1. Context
1.1.1. Low-Level and You have probably used computers to do all sorts of useful and interesting things. In each application, the computer responds in
High-Level Computer different ways to your input, from the keyboard, mouse or a file. Still the underlying operations are determined by the design of
Operations
the program you are given. In this set of tutorials you will learn to write your own computer programs, so you can give the
1.1.2. Why Python
1.1.3. Obtaining Python computer instructions to react in the way you want.
for Your Computer
1.1.4. Philosophy and
Implementation of the
Hands-On Python 1.1.1. Low-Level and High-Level Computer Operations
Tutorials
1.1.5. Using the Tutorial First let us place Python programming in the context of the computer hardware. At the most fundamental level in the computer
- Text and Video there are instructions built into the hardware. These are very simple instructions, peculiar to the hardware of your particular type
1.1.6. Learning to of computer. The instructions are designed to be simple for the hardware to execute, not for humans to follow. The earliest
Problem-Solve
programming was done with such instructions. If was difficult and error-prone. A major advance was the development of higher-
Previous topic level languages and translators for them. Higher-level languages allow computer programmers to write instructions in a format
1. Beginning With Python that is easier for humans to understand. For example

Next topic z = x+y


1.2. The Python Interpreter
and Idle, Part I is an instruction in many high-level languages that means something like:

This Page 1. Access the value stored at a location labeled x


Show Source 2. Calculate the sum of this value and the value stored at a location labeled y
3. Store the result in a location labeled z.
Quick search
No computer understands the high-level instruction directly; it is not in machine language. A special program must first translate
instructions like this one into machine language. This one high-level instruction might be translated into a sequence of three
Go
machine language instructions corresponding to the three step description above:
Enter search terms or a module,
0000010010000001
class or function name. 0000000010000010
0000010110000011

Obviously high-level languages were a great advance in clarity!

If you follow a broad introduction to computing, you will learn more about the layers that connect low-level digital computer
circuits to high-level languages.

1.1.2. Why Python


There are many high-level languages. The language you will be learning is Python. Python is one of the easiest languages to
learn and use, while at the same time being very powerful: It is used by many of the most highly productive professional
programmers. A few of the places that use Python extensively are Google, the New York Stock Exchange, Industrial Light and
Magic, .... Also Python is a free language! If you have your own computer, you can download it from the Internet....
1.1.3. Obtaining Python for Your Computer
Even if you have Python on your own computer, you may well not have the latest version. Go to the Downloads page below and
check on the latest version. The instructions for individual operating systems describe how to check if you think you already
have what you need.

Go to the Downloads page linked to http://www.python.org. Be careful to choose the version for your operating system and
hardware. Choose the latest stable version. You do not need a source code distribution.

Do not choose a version 2.X, which is incompatible. (Version 2.6 and 2.7 are described in an older version of this tutorial.)

Windows
If you think you already have a current Python set to go, you can check by getting the examples, Your Python Folder and
Python Examples. Then try starting Idle: Starting Idle. If Idle starts, see if the version matches the latest version of Python. If
so, skip the part below!

Note: there is one variation from the default installation below: Read everything before completing the installation.

Make sure the version is at least 3.4 or later, and use a download package with the MSI installer, if it is available. If you do
not know that your machine is using a 64 bit version of the operating system, a 32 bit version is safer.

Except in one case, you just need to execute the installer, and interact enough to agree to all the default choices: On the
step choosing components to install, if there is an X beside the last component, putting Python in your path, you are
encouraged to click on that component and include it.

If you forget this one variation from the defaults, you can return to the installation and repair it.

OS X
If you think you already have a current Python set to go, then try starting Idle: Starting Idle. If Idle starts, see if the version
matches the latest version of Python. If so, skip the part below!

Double-click on the installer for your version of OS X. Find and run the MacPython.mpkg that is inside. Follow the defaults
for installation.

Note that Python uses a Tcl/Tk library, and Macs come with a version, however, for some versions of the operating system,
a different version of Tcl/Tk is needed. Be sure to see http:http://www.python.org/download/mac/tcltk/ and see if you need to
do a separate download listed there.

Linux
An older version of Python is generally installed, and even if a current version, 3.1+, is installed, Idle is not always installed.
Look for a package to install, something like ‘idle-python’ (the name in the Ubuntu distribution).

1.1.4. Philosophy and Implementation of the Hands-On Python Tutorials


Although Python is a high-level language, it is not English or some other natural human language. The Python translator does
not understand “add the numbers two and three”. Python is a formal language with its own specific rules and formats, which
these tutorials will introduce gradually, at a pace intended for a beginner. These tutorials are also appropriate for beginners
because they gradually introduce fundamental logical programming skills. Learning these skills will allow you to much more
easily program in other languages besides Python. Some of the skills you will learn are

breaking down problems into manageable parts


building up creative solutions
making sure the solutions are clear for humans
making sure the solutions also work correctly on the computer.

Guiding Principals for the Hands-on Python Tutorials:

The best way to learn is by active participation. Information is principally introduced in small quantities, where your active
participation, experiencing Python, is assumed. In many place you will only be able to see what Python does by doing it
yourself (in a hands-on fashion). The tutorial will often not show. Among the most common and important words in the
tutorial are “Try this:”

Other requests are for more creative responses. Sometimes there are Hints, which end up as hyperlinks in the web page
version, and footnote references in the pdf version. Both formats should encourage you to think actively about your
response first before looking up the hint. The tutorials also provide labeled exercises, for further practice, without
immediate answers provided. The exercises are labeled at three levels

No label
Immediate reinforcement of basic ideas - preferably do on your first pass.

*
Important and more substantial - be sure you can end up doing these. Allow time to do them!

**
Most creative

Information is introduced in an order that gives you what you need as soon as possible. The information is presented in
context. Complexity and intricacy that is not immediately needed is delayed until later, when you are more experienced.

In many places there are complications that are important in the beginning, because there is a common error caused by a
slight misuse of the current topic. If such a common error is likely to make no sense and slow you down, more information
is given to allow you to head off or easily react to such an error.

Although this approach is an effective way to introduce material, it is not so good for reference. Referencing is addressed in
several ways:

Detailed Table of Contents


Extensive Index
Flexible Search Engine built into the html version (does not work on an html version that you download to your computer)
Cross references to sections that elaborate on an introductory section. Hyperlinks allow you to jump between the
referenced parts in the html version or the pdf version viewed on a computer. The pdf version also gives page references.
Concise chapter summaries, grouping logically related items, even if that does not match the order of introduction.

1.1.5. Using the Tutorial - Text and Video


The Hands-on Python Tutorial was originally a document to read, with both the html version and a pdf version. Even if you do not
print it, some people use the pdf version online, preferring its formatting to the formatting in the html version.
Some people learn better visually and verbally from the very beginning. The Tutorial has 800 by 600 pixel videos for many
sections.

Also mentioned for the convenience of my Comp 150 class are videos beyondPython, for the part of the class after Python.

The videos are in two places:

Box.com: https://luc.box.com/comp150video

Download split in 5 parts, with no ID needed at all. The four chapters of the Hands-on Python Tutorial and beyondPython
are collected in zip files for you to download, and then expand the zip files before using.

Google Drive:
https://drive.google.com/a/cs.luc.edu/#folders/0B5WvvnDHeaIYMGE2MzU4OWEtYzQ4Zi00YzhiLTliMTItNjRjYzMyYzgyMTk2

You need a Google Drive/Docs login ID. If you are not already logged into Google Drive/Docs, you will need to do it when
you click on the link. If you have that ID, then the advantage of Google Drive is that you can select exactly what parts to
download in one long download (548 MB total), and you do not need to separately unzip. This may not work with Internet
Explorer, but it does work with Firefox, Safari or Chrome browser.

If you want all the videos for the course click on the box in front of the Title line to get all the folders under comp150video.
If you want less, drill down to the parts you want, and click in the boxes in front of them.

Then in the lower headings, to the right of the Trash can icon, click on the More heading. Select Download.... Leave file
types As Is, and click on the download button. This is best done before going to sleep for the night if you selected most of
the course!

From either site, the videos are also runnable by clicking on one and streaming from the Internet, but they only delivers a smaller
version of the image, leaving the audio fine, but making text hard or imposible to read. If the video is following along with the
tutorial text, and you look at the full sized web page while listening, the video size may sometimes not be an issue. The Box
version is also likely to be slow to start from online. The downloaded versions will display in full size and resolution.

To get the most out of the tutorial, I strongly suggest the following approach for each part:

Watch a video if you like. They are clearly labeled by numerical section. Stop the video where I ask you to think. The
videos hit the high points and take advantage of being able to point at specific places on the screen.

Some details may only appear in the written text.

Stop the video frequently to test things for yourself! If a new function is introduced, do not only watch the video, but try it
out for yourself, including with data not in the video. In some places the written version mentions more examples to try. I
suggest looking at the written version after each video.

Whether you look at the video of a section or not, do look through a written version, either as a first pass or to review and
fill in holes from the videos. Be sure to stop and try things yourself, and see how they actually work on your computer.

Look at the labeled exercises. You are strongly recommended to give the unstarred ones an immediate try to reinforce
basic concepts. The starred ones (*) are important for a strong understanding. Do not get too far ahead in
reading/watching before trying the starred exercises. Understanding earlier parts well enough to be able to solve problems
is going to either be completely necessary for understanding some later sections or at least make later sections easier to
follow and fully comprehend.

Python provides too rich an environment to be able to show you all interrelationships immediately. That can mean errors
send you in a strange (to you) direction. I cannot head them all off for you in this text. Do not be afraid to ask for help from
your instructor or another more experienced Python programmer if you look and look again at something and are still
stuck. The frequency of such situations should decrease as you get more experience!

Have fun and be creative, and discover your power with Python!

1.1.6. Learning to Problem-Solve


While the tutorial introduces all the topics, there is more to say about using it effectively. There is way too much detail to just
absorb all at once, So what are the first things to learn?

More important than memorizing details is having an idea of the building blocks available and how they are useful. For the most
direct exercises, you might just look back over the most recent section looking for related things, but that will not work when you
have scores of sections that might have useful parts! The basic idea of the building blocks should be in your head. For instance,
looking ahead to when you have finished the Tutorial through 1.10.4, you will want to have these ideas very present in your
head:

You can use numbers and do arithmetic.


You can store and retrieve data using variable names and assignment statements.
Python has many useful built-in functions that can affect the system or return results for you to use.
You can get keyboard input from the user and print things back for the user.
Data comes in different types, and you can convert where it makes sense.
You can use strings and generate them in many ways: literal strings, concatenation operator (+), string format method.

Once you have an idea of the appropriate building blocks needed to solve a specific problem, then you can worry about more
details. Particularly at the beginning, you are not likely to have all the exact Python syntax for the parts of your solution nailed
down! It is not important to remember it precisely, but it is important to know how to find a clear explanation efficiently: Know the
location in examples or in the tutorial, or use the index, the search capacity, summaries, and/or write things in notes for yourself
- as for an exam. Writing short bits down is also useful because the act of writing helps many people absorb what they are
writing.

As your experience increases you will get used to and remember more and more stuff, but there is always the latest idea/syntax
to get used to! Your notes of what is important, but still not in immediate recall, will evolve continuously.

This multi-tiered approach means that what you absorb should not just be an enormous collection of unstructured facts that you
plumb through in its entirety to find a useful idea. You first need to be particularly aware of the major headings of useful
features, and then do what you need to drill down to details as necessary.

This approach is important in the real-world, away from Python. The world is awash with way to much information to memorize,
but you must access the information that you need to synthesize and formulate solutions/arguments ... effectively!

Knowing all the building blocks of a solution is obviously important. Many successful holistic thinkers can do this effectively. In
some domains a knowledge of the pieces and their relationships is enough. Programming requires more than this, however: It is
critical to sort out the exact sequence in which the pieces must logically appear. Some excellent holistic thinkers have a hard
time with this sequencing, and must pay extra attention when planning code. If you are like this, be patient and be prepared to
ask for help where needed.

What to do after you finish an exercise is important, too. The natural thing psychologically, particularly if you had a struggle, is to
think, “Whew, outta here!!!!”

On something that came automatically or flowed smoothly, that is not a big deal - you will probably get it just as fast the next
time. If you had a hard time and only eventually got to success, you may be doing yourself a disservice with “Whew, outta
here!!!”

We have already mentioned how not everything is equally important, and some things are more important to keep in your head
than others. The same idea applies to all the steps in solving a possibly long problem. Some parts were easy; some were hard;
there may have been several steps. If all of that goes into your brain in one continuous stream of stuff that you remember at the
same level, then you are going to leave important nuggets mixed in with an awful lot of unimportant and basically useless
information. Then the information is likely to all fade into oblivion, or be next to impossible to cycle through looking for the useful
nuggets. Why do the problem anyway if you are just going to bury important information further down in your brain?

What is important? The most obvious thing you will need at a higher level of recall is what just messed you up, what you missed
until doing this problem: After finishing the actual problem, actively follow up and ask yourself:

What did I get in the end that I was missing initially? What was the connection I made?
Does this example fit in to some larger idea/abstraction/generalization in a way that I did not see before?
How am I going to look at this so I can make a similar connection in a similar (or maybe only partly similar) problem?
Is there a kernel here that I can think of as a new tool in my bag of tricks?

Your answers to these questions are the most important things to take away from your recent hard work. The extra consideration
puts them more in the “priority” part of your brain, so you can really learn from your effort. When you need the important ideas
next, you do not need to play through all the details of the stuff you did to solve the earlier problem.

Keep coming back to this section and check up on your process: It is really important.

«
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on May 15, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Table Of Contents 1.2. The Python Interpreter and Idle,


1.2. The Python Interpreter
and Idle, Part I Part I
1.2.1. Your Python
Folder and Python
Examples
1.2.2. Running A 1.2.1. Your Python Folder and Python
Sample Program
1.2.3. A Sample
Examples
Program, Explained
1.2.4. Starting Idle First you need to set up a location to store your work and the example
1.2.5. Windows in Idle programs from this tutorial. If you are on a Windows computer, follow
just one of the three choices below to find an appropriate place to
Previous topic download the example archive examples.zip, and then follow the later
1.1. Context instructions to unzip the archive.
Next topic
Your Own Computer
1.3. Whirlwind Introduction
To Types and Functions If you are at your own computer, you can put the folder
for your Python programs most anywhere you like. For
This Page Chapter 4, it will be important that none of the directories
Show Source leading down to your Python folder contain any blanks in
them. In particular in Windows, “My Documents” is a bad
Quick search location. In Windows you can create a directory in C:
drive, like C:\myPython. You should have installed
Python to continue. This also means there is a problem
Go
putting the folder under your home folder if you put a
Enter search terms or a module, blank in the login ID.
class or function name.
Your Flash Drive
If you do not have your own computer, or you want to
have your materials easily travel back and forth between
the lab and home, you will need a flash drive. Plug your
flash drive into the computer USB port. Please Note:
Flash drives are easy for me to forget and leave in the
computer. I have lost a few this way. If you are as
forgetful as I, you might consider a string from the flash
drive to something you will not forget to take with you.
Open My Computer (on the desktop) to see where the
flash drive is mounted, and open that drive.
Temporary
If you (temporarily) do not have a flash drive and you are
at a Loyola lab computer: Open My Computer from the
desktop, and then select create a folder with your name
or initials to make it easy for you to save and remove
things. Change to that folder. You should place the
examples archive here. You will need to save your work
somehow before you log off of the computer. You may
want to email individual files to yourself, or rezip the
examples folder and send just the one archive file to
yourself each time until you remember a flash drive!

In Windows, after you have chosen a location for the archive,


examples.zip, download it by right clicking on
http://anh.cs.luc.edu/python/hands-on/3.1/examples.zip and selecting
“Save As” or the equivalent on your browser and then navigate to save
the archive to the chosen location on your computer. Note that the
examples, like this version of the tutorial, are for Python 3.1 and later.
There were major changes to Python in version 3.0, making it
incompatible with earlier versions.

If you are using Python version 2.7 for some good reason, you should
continue with the older version of the tutorial. Go to
http://anh.cs.luc.edu/python/hands-on and find the links to the proper
version of the tutorial and examples.

Once you have the archive, you need to extract the files. Make a file
browser window set to the directory where you put the zip file. Then:

Windows
Right click on examples.zip, select Extract All. This will create the
folder examples . End up with a file browser window showing the
contents of the examples folder. This will be your Python folder in
later discussion.
Mac
Double click on the zip file and the expanded examples folder
appears.

Warning: On Windows, files in a zip archive can be viewed while


they are still in the zip archive. Modifying and adding files is not so
transparent. Be sure that you unzip the archive and work from the
regular directory that holds the resulting unzipped files.

Warning: Make sure that all the directories leading down to your
Python examples directory do not include any spaces in them. This
will be important in Chapter 4 for the local web server. In particular,
that means you should not place your folder under “My Documents”. A
directory like C:\hands-on or C:\python would be fine.

You also have the option of downloading

An archive containing the web version of the tutorial


http://anh.cs.luc.edu/python/hands-on/3.1/handsonHtml.zip for
local viewing, without the Internet. Download it and unzip as with
the examples. The local file to open in your browser in in the
handsonHtml folder you unzipped and the main web page file to
open is called index.html .
The PDF version of the tutorial for printing
http://anh.cs.luc.edu/python/hands-on/3.1/Hands-
onPythonTutorial.pdf. Some people also like the typography of this
version on the web, too. It is hyperlinked like html version.

The disadvantage of a local copy is that the tutorial may be updated


online after you get your download. The change log file
http://anh.cs.luc.edu/python/hands-on/3.1/changelog.html will show a
summary of any major changes.

1.2.2. Running A Sample Program


This section assumes Python, version at least 3.1, is already on your
computer. Windows does not come with Python. (To load Python see
Obtaining Python for Your Computer.)

Before getting to the individual details of Python, you will run a simple
text-based sample program. Find madlib.py in your Python folder, set up
in Your Python Folder and Python Examples.

Options for running the program:

If you have trouble with the following options, opening the program
in Idle is discussed in Starting Idle.

In Windows, you can display your folder contents, and double click
on madlib.py to start the program.

On a Mac, you can Ctrl-click on madlib.py in the Finder, select


Open With, and choose the Python Launcher for your Python 3
version. When you are done with the program, close the terminal
window.

In Linux you may be able to open a terminal window, change into


your Python examples directory, and enter the command

python madlib.py

or possibly to distinguish Python 2 and 3:

python3 madlib.py

If neither of these work, get help.

You can also start idle from a Windows command window. You will want
the current directory to be where your programs are. You can enter the
full path to Python, for instance:
C:\python34\python madlib.py

or if python is in your system’s path, you can skip the part before
“python”. In whatever manner you start the program, run it, responding
to the prompts on the screen. Be sure to press the enter key at the end
of each response requested from you.

Try the program a second time and make different responses.

1.2.3. A Sample Program, Explained


If you want to get right to the detailed explanations of writing your own
Python, you can skip to the next section Starting Idle. If you would like
an overview of a working program, even if all the explanations do not
make total sense yet, read on.

Here is the text of the madlib.py program, followed by line-by-line brief


explanations. Do not worry if you not totally understand the
explanations! Try to get the gist now and the details later. The numbers
on the right are not part of the program file. They are added for
reference in the comments below:

#! /usr/bin/env python3 0
''' 1
String Substitution for a Mad Lib 2
Adapted from code by Kirby Urner 3
''' 4
5
storyFormat = ''' 6
Once upon a time, deep in an ancient jungle, 7
there lived a {animal}. This {animal} 8
liked to eat {food}, but the jungle had 9
very little {food} to offer. One day, an 10
explorer found the {animal} and discovered 11
it liked {food}. The explorer took the 12
{animal} back to {city}, where it could 13
eat as much {food} as it wanted. However, 14
the {animal} became homesick, so the 15
explorer brought it back to the jungle, 16
leaving a large supply of {food}. 17
18
The End 19
''' 20
21
def tellStory(): 22
userPicks = dict() 23
addPick('animal', userPicks) 24
addPick('food', userPicks) 25
addPick('city', userPicks) 26
story = storyFormat.format(**userPicks) 27
print(story) 28
29
def addPick(cue, dictionary): 30
'''Prompt for a user response using the cue string, 31
and place the cue-response pair in the dictionary. 32
''' 33
prompt = 'Enter an example for ' + cue + ': ' 34
response = input(prompt) 35
dictionary[cue] = response 36
37
tellStory() 38
input('Press Enter to end the program.') 39

Line By Line Explanation

#! /usr/bin/env python3 0

This is not technically a part of the program. It is there to tell the


operating system what version of Python to choose, since the older
Python 2 is incompatible with the newer Python 3. We will mostly run
programs from inside the Idle programming environment, where this line
is not needed. To run just by clicking on the program in an operating
system window, however, the line is important if your computer also has
Python 2.

''' 1
String Substitution for a Mad Lib 2
Adapted from code by Kirby Urner 3
''' 4

1-4: There is multi-line text enclosed in triple quotes. Quoted text is


called a string. A string at the very beginning of a program like this is
documentation for the file.

5,21,29,37: Blank lines are included for human readability to separate


logical parts. The computer ignores the blank lines.

storyFormat = ''' 6
Once upon a time, deep in an ancient jungle, 7
there lived a {animal}. This {animal} 8
liked to eat {food}, but the jungle had 9
very little {food} to offer. One day, an 10
explorer found the {animal} and discovered 11
it liked {food}. The explorer took the 12
{animal} back to {city}, where it could 13
eat as much {food} as it wanted. However, 14
the {animal} became homesick, so the 15
explorer brought it back to the jungle, 16
leaving a large supply of {food}. 17
18
The End 19
''' 20

6: The equal sign tells the computer that this is an assignment statement.
The computer will now associate the value of the expression between
the triple quotes, a multi-line string, with the name on the left,
storyFormat .

7-20: These lines contain the body of the string and the ending triple
quotes. This storyFormat string contains some special symbols making it
a format string, unlike the string in lines 1-4. The storyFormat string will
be used later to provide a format into which substitutions are made. The
parts of the string enclosed in braces are places a substitute string will
be inserted later. The substituted string will come from a custom
dictionary that will contain the user’s definitions of these words. The
words in the braces: {animal} , {food} , {city} , indicate that animal , food ,
and city are words in a dictionary. This custom dictionary will be
created in the program and contain the user’s definitions of these words.
These user’s definitions will be substituted later in the format string
where each {...} is currently.

def tellStory(): 22
userPicks = dict() 23
addPick('animal', userPicks) 24
addPick('food', userPicks) 25
addPick('city', userPicks) 26
story = storyFormat.format(**userPicks) 27
print(story) 28

22: def is short for definition. This line is the heading of a definition,
which makes the name tellStory becomes defined as a short way to
refer to the sequence of statements that start indented on line 23, and
continue through line 28.

23: The equal sign tells the computer that this is another assignment
statement. The computer will now associate the name userPicks with a
new empty dictionary created by the Python code dict() .

24-26: addPick is the name for a sequence of instructions defined on


lines 29-31 for adding another definition to a dictionary, based on the
user’s input. The result of these three lines is to add definitions for each
of the three words animal , food , and city to the dictionary called
userPicks .

27: Assign the name story to a string formed by substituting into


storyFormat using definitions from the dictionary userPicks, to give the
user’s customized story.

28: This is where all the work becomes visible: Print the story string to
the screen.

def addPick(cue, dictionary): 30


'''Prompt for a user response using the cue string, 31
and place the cue-response pair in the dictionary. 32
''' 33
prompt = 'Enter an example for ' + cue + ': ' 34
response = input(prompt) 35
dictionary[cue] = response 36

30: This line is the heading of a definition, which gives the name addPick
as a short way to refer to the sequence of statements indented on line
34-36. The name addPick is followed by two words in parenthesis, cue
and dictionary . These two words are associated with an actual cue
word and dictionary given when this definition is invoked in lines 24-26.

31-33: A documentation comment for the addPick definition.

34: The plus sign here is used to concatenate parts of the string
assigned to the name prompt . The current value of cue is placed into the
string.

35: The right-hand-side of this equal sign causes an interaction with the
user, to input text from the keyboard: The prompt string is printed to the
computer screen, and the computer waits for the user to enter a line of
text. That line of text then becomes a string inside the program. This
string is assigned to the name response .

36: The left-hand-side of the equal sign is a reference to the definition of


the cue word in the dictionary. The whole line ends up making the
definition of the current cue word become the response typed by the
user.

tellStory() 38
input('Press Enter to end the program.') 39

38: The definition of tellStory above does not make the computer do
anything besides remember what the instruction tellStory means. It is
«
only in this line, with the name, tellStory , followed by parentheses, that
the whole sequence of remembered instructions are actually carried out.

39: This line is only here to accommodate running the program in


Windows by double clicking on its file icon. Without this line, the story
would be displayed and then the program would end, and Windows
would make it immediately disappear from the screen! This line forces
the program to continue being displayed until there is another response
from the user, and meanwhile the user may look at the output from
tellStory .

1.2.4. Starting Idle


The program that translates Python instructions and then executes them
is the Python interpreter.

This interpreter is embedded in a number of larger programs that make it


particularly easy to develop Python programs. Such a programming
environment is Idle, and it is a part of the standard distribution of Python.

Read the section that follows for your operating system:


Windows
(Assuming you already have Python installed.) Display
your Python folder. You should see icons for
Idle34Shortcut for Python 3.4, Idle33Shortcut for Python
3.3, (and maybe a similar icon with a version after 3.4 -
ignore any that do not correspond to your version of
Python). Double click on the appropriate shortcut, and an
Idle window should appear. After this the instructions are
the same in any operating environment.

CAUTION: Do not move the shortcut to your desktop. It


should to be in the folder with the examples. Instead you
might put a shortcut to this examples folder on your
desktop.

An alternative for opening an existing Python program in


Python 3+ is to go to an operating system directory
window and use the context menu that you get by right-
clicking, and select Open With Idle. This alternative
depends on the operating system being set up to
recognize the Python file type, .py.

Mac
With OS X a simple approach is to open Python files
ending in .py into an Edit window by selecting them
directly in an operating system finder window. From
there you can create or open other files.

You should also be able to open Idle from Spotlight.

There is more special Mac information in


http://anh.cs.luc.edu/python/hands-
on//3.1/pythonOnMac.html.

Linux
The approach depends on the installation. In Ubuntu, you
should find Idle in the Programming section of the
Applications menu. You are better starting idle from a
terminal, with the current directory being your Python
folder. You may need a special name set up to
distinguish idle for versions 2 and 3, for instance idle3 for
version 3.X.

The heading data when you open Idle shows the exact version, like
3.4.0. You can see if it matches the latest current version of Python.

1.2.5. Windows in Idle


Idle has several parts you may choose to display, each with its own
window. Depending on the configuration, Idle can start up showing
either of two windows, an Edit Window or a Python Shell Window. You
are likely to first see an Edit window, whose top left corner looks
something like in Windows:

For more on the Edit Window, see The Idle Editor and Execution.

If you see this Edit Window with its Run menu on top, go to the Run
menu and choose PYTHON SHELL to open a Python Shell Window for
now. Then you may close the Edit Window.

Either initially, or after explicitly opening it, you should now see the
Python Shell window, with a menu like the following, though the text may
be slightly different:

Look at the Python Shell. ...

In the Shell the last line should look like

>>>

The >>> is the prompt, telling you Idle is waiting for you to type
something. Continuing on the same line enter

6+3

Be sure to end with the Enter key. After the Shell responds, you should
see something like
>>> 6+3
9
>>>

The shell evaluates the line you entered, and prints the result. You see
Python does arithmetic. At the end you see a further prompt >>> where
you can enter your next line.... The result line, showing 9 , that is
produced by the computer, does not start with >>> .
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 25, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Previous topic 1.3. Whirlwind Introduction To Types


1.2. The Python Interpreter
and Idle, Part I and Functions
Next topic Python directly recognizes a variety of types of data. Here are a few:
1.4. Integer Arithmetic
Numbers: 3 , 6 , -7 , 1.25
This Page
Show Source Character strings: 'hello' , 'The answer is: '

Quick search Lists of objects of any type: [1, 2, 3, 4] , ['yes', 'no',


'maybe']

Go
A special datum meaning nothing: None
Enter search terms or a module,
class or function name. Python has large collection of built-in functions that operate on different
kinds of data to produce all kinds of results. To make a function do its
action, parentheses are required. These parentheses surround the
parameter or parameters, as in a function in algebra class.

The general syntax to execute a function is

functionName ( parameters )

One function is called type , and it returns the type of any object. The
Python Shell will evaluate functions. In the Shell the last line should look
like

>>>

Continuing on the same line enter

type(7)

Always remember to end with the Enter key. After the Shell responds,
you should see something like

>>> type(7)
<class 'int'>
>>>

In the result, int is short for integer. The word class is basically a
synonym for type in Python.

Note that the line with the value produced by the shell does not start with
>>> and appears at the left margin. Hence you can distinguish what you
type (after the >>> prompt) from what the computer responds.
At the end you see a further prompt where you can enter your next line....

For the rest of this section, at the >>> prompt in the Python Shell,
individually enter each line below that is set off in typewriter font. So
next enter

type(1.25)

Note the name in the last result is float , not real or decimal, coming from
the term “floating point”, for reasons that will be explained later, in
Floats, Division, Mixed Types.

Enter

type('hello')

« In your last result you see another abbreviation: str rather than string.
Enter

type([1, 2, 3])

Strings and lists are both sequences of parts (characters or elements).


We can find the length of that sequence with another function with the
abbreviated name len . Try both of the following, separately, in the Shell:

len([2, 4, 6])
len('abcd')

Some functions have no parameters, so nothing goes between the


parentheses. For example, some types serve as no-parameter functions
to create a simple value of their type. Try

list()

You see the way an empty list is displayed.

Functions may also take more than one parameter. Try

max(5, 11, 2)

Above, max is short for maximum.

Some of the names of types serve as conversion functions (where there


is an obvious meaning for the conversion). Try each of the following, one
at a time, in the Shell:

str(23)
int('125')
An often handy Shell feature: an earlier Shell line may to copied and
edited by clicking anywhere in the previously displayed line and then
pressing the Enter key. For instance you should have entered several
lines starting with len . click on any one, press Enter , and edit the line for
a different test.

Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 24, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Table Of Contents 1.4. Integer Arithmetic


1.4. Integer Arithmetic
1.4.1. Addition and
Subtraction
1.4.2. Multiplication, 1.4.1. Addition and Subtraction
Parentheses, and
Precedence We start with the integers and integer arithmetic, not because arithmetic
1.4.3. Division and is exciting, but because the symbolism should be mostly familiar. Of
Remainders course arithmetic is important in many cases, but Python is probably
Previous topic more often used to manipulate text and other sorts of data, as in the
sample program in ref:Running-A-Sample.
1.3. Whirlwind Introduction
To Types and Functions
Python understands numbers and standard arithmetic. For the whole
Next topic section on integer arithmetic, where you see a set-off line in typewriter
1.5. Strings, Part I font, type individual lines at the >>> prompt in the Python Shell. Press
Enter after each line to get Python to respond:
This Page
Show Source 77
2 + 3
Quick search 5 - 7

Python should evaluate and print back the value of each expression. Of
Go
course the first one does not require any calculation. It appears that the
shell just echoes back what you printed.
Enter search terms or a module,
class or function name.
The Python Shell is an interactive interpreter. As you can see, after you
press Enter, it is evaluating the expression you typed in, and then
printing the result automatically. This is a very handy environment to
check out simple Python syntax and get instant feedback. For more
elaborate programs that you want to save, we will switch to an Editor
Window later.

1.4.2. Multiplication, Parentheses, and


Precedence
Try in the Shell:

2 x 3

You should get your first syntax error. The x should have become
highlighted, indicating the location where the Python interpreter
discovered that it cannot understand you: Python does not use x for
multiplication as you may have done in grade school. The x can be
confused with the use of x as a variable (more on that later).
Instead the symbol for multiplication is an asterisk * . Enter each of the
following. You may include spaces or not. The Python interpreter can
figure out what you mean either way. Try in the Shell:

2*5
2 + 3 * 4

If you expected the last answer to be 20, think again: Python uses the
normal precedence of arithmetic operations: Multiplications and divisions
are done before addition and subtraction, unless there are parentheses.
Try

(2+3)*4
2 * (4 - 1)

Now try the following in the Shell, exactly as written, followed by Enter,
with no closing parenthesis:

5 * (2 + 3

Look carefully. There is no answer given at the left margin of the next line
and no prompt >>> to start a new expression. If you are using Idle, the
cursor has gone to the next line and has only indented slightly. Python is
waiting for you to finish your expression. It is smart enough to know that
opening parentheses are always followed by the same number of
closing parentheses. The cursor is on a continuation line. Type just the
matching close-parenthesis and Enter,

« and you should finally see the expression evaluated. (In some versions
of the Python interpreter, the interpreter puts ‘...’ at the beginning of a
continuation line, rather than just indenting.)

Negation also works. Try in the Shell:

-(2 + 3)

1.4.3. Division and Remainders


If you think about it, you learned several ways to do division. Eventually
you learned how to do division resulting in a decimal. Try in the Shell:

5/2
14/4

As you saw in the previous section, numbers with decimal points in them
are of type float in Python. They are discussed more in Floats, Division,
Mixed Types.

In the earliest grades you would say “14 divided by 4 is 3 with a


remainder of 2”. The problem here is that the answer is in two parts, the
integer quotient 3 and the remainder 2, and neither of these results is
the same as the decimal result. Python has separate operations to
generate each part. Python uses the doubled division symbol // for the
operation that produces just the integer quotient, and introduces the
symbol % for the operation of finding the remainder. Try each in the
Shell:

14/4
14//4
14%4

Now predict and then try each of

23//5
23%5
20%5
6//8
6%8
6/8

Finding remainders will prove more useful than you might think in the
future!
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Table Of Contents 1.5. Strings, Part I


1.5. Strings, Part I
1.5.1. String Delimiters, Enough with numbers for a while. Strings of characters are another
Part I important type in Python.
1.5.2. String
Concatenation
1.5.2.1. String
Exercise 1.5.1. String Delimiters, Part I
Previous topic
A string in Python is a sequence of characters. For Python to recognize a
1.4. Integer Arithmetic
sequence of characters, like hello , as a string, it must be enclosed in
Next topic quotes to delimit the string.
1.6. Variables and
Assignment For this whole section on strings, continue trying each set-off line of code
in the Shell. Try
This Page
Show Source "hello"

Quick search Note that the interpreter gives back the string with single quotes. Python
does not care what system you use. Try
Go
'Hi!'
Enter search terms or a module,
class or function name. Having the choice of delimiters can be handy.

Figure out how to give Python the string containing the text: I'm happy.
Try it. If you got an error, try it with another type of quotes, and figure out
why that one works and not the first.

There are many variations on delimiting strings and embedding special


symbols. We will consider more ways later in Strings Part II.

Note: A string can have any number of characters in it, including 0.


The empty string is '' (two quote characters with nothing between
them). Many beginners forget that having no characters is legal. It can
be useful.

Strings are a new Python type. Try

type('dog')
type('7')
type(7)

The last two lines show how easily you can get confused! Strings can
include any characters, including digits. Quotes turn even digits into
strings. This will have consequences in the next section....
1.5.2. String Concatenation
Strings also have operation symbols. Try in the Shell (noting the space
after very ):

'very ' + 'hot'

The plus operation with strings means concatenate the strings. Python
looks at the type of operands before deciding what operation is
associated with the +.

Think of the relation of addition and multiplication of integers, and then


guess the meaning of

3*'very ' + 'hot'

Were you right? The ability to repeat yourself easily can be handy.
«
Predict the following and then test. Remember the last section on types:

7+2
'7'+'2'

Python checks the types and interprets the plus symbol based on the
type. Try

'7'+2

With mixed string and int types, Python sees an ambiguous expression,
and does not guess which you want - it just gives an error! [1]

This is a traceback error. These occur when the code is being executed.
In the last two lines it shows the line where the error was found, and
then a reason for the error. Not all reasons are immediately intelligible to
a starting programmer, but they are certainly worth checking out. In this
case it is pretty direct. You need to make an explicit conversion, so both
are strings if you mean concatenation, '7' + str(2) , or so both are int if
you mean addition, int('7') + 2 .

With literal strings these examples are only useful for illustration: There is
no reason to write such verbose expressions when you already know
the intended result. With variables, starting in the next section,
expressions involving these conversions become more important....

1.5.2.1. String Exercise


Figure out a compact way to get Python to make the string,
YesYesYesYesYes , and try it. How about MaybeMaybeMaybeYesYesYesYesYes ?
Hint: [2]

[1] Be careful if you are a Java or C# programmer! This is unlike those


languages, where the 2 would be automatically converted to ‘2’ so
the concatenation would make sense.
[2] Hint for the second one: use two *’s and a +.

Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Mar 06, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Table Of Contents 1.6. Variables and Assignment


1.6. Variables and
Assignment Each set-off line in this section should be tried in the Shell.
1.6.1. Literals and
Identifiers Try
Previous topic
width = 10
1.5. Strings, Part I

Next topic Nothing is displayed by the interpreter after this entry, so it is not clear anything
1.7. Print Function, Part I happened. Something has happened. This is an assignment statement, with a
variable, width , on the left. A variable is a name for a value. An assignment
This Page statement associates a variable name on the left of the equal sign with the value
Show Source of an expression calculated from the right of the equal sign. Enter

Quick search width

Go
Once a variable is assigned a value, the variable can be used in place of that
value. The response to the expression width is the same as if its value had been
Enter search terms or a module, entered.
class or function name.
The interpreter does not print a value after an assignment statement because the
value of the expression on the right is not lost. It can be recovered if you like,
by entering the variable name and we did above.

Try each of the following lines:

height = 12
area = width * height
area

The equal sign is an unfortunate choice of symbol for assignment, since


Python’s usage is not the mathematical usage of the equal sign. If the symbol
had appeared on keyboards in the early 1990’s, it would probably have been
used for assignment instead of =, emphasizing the asymmetry of assignment. In
mathematics an equation is an assertion that both sides of the equal sign are
already, in fact, equal. A Python assignment statement forces the variable on
the left hand side to become associated with the value of the expression on the
right side. The difference from the mathematical usage can be illustrated. Try:

10 = width

so this is not equivalent in Python to width = 10 . The left hand side must be a
variable, to which the assignment is made. Reversed, we get a syntax error.
Try

width = width + 5
This is, of course, nonsensical as mathematics, but it makes perfectly good sense
as an assignment, with the right-hand side calculated first. Can you figure out
the value that is now associated with width? Check by entering

width

In the assignment statement, the expression on the right is evaluated first. At that
point width was associated with its original value 10, so width + 5 had the
value of 10 + 5 which is 15. That value was then assigned to the variable on the
left ( width again) to give it a new value. We will modify the value of variables
in a similar way routinely.

Assignment and variables work equally well with strings. Try:

first = 'Sue'
last = 'Wong'
name = first + ' ' + last
name

Try entering:

first = fred

Note the different form of the error message. The earlier errors in these tutorials
were syntax errors: errors in translation of the instruction. In this last case the
syntax was legal, so the interpreter went on to execute the instruction. Only
then did it find the error described. There are no quotes around fred , so the
interpreter assumed fred was an identifier, but the name fred was not defined at
the time the line was executed.

It is both easy to forget quotes where you need them for a literal string and to
mistakenly put them around a variable name that should not have them!

Try in the Shell:

fred = 'Frederick'
first = fred
first

Now fred , without the quotes, makes sense.

There are more subtleties to assignment and the idea of a variable being a “name
for” a value, but we will worry about them later, in Issues with Mutable
Objects. They do not come up if our variables are just numbers and strings.

Autocompletion: A handy short cut. Idle remembers all the variables you
have defined at any moment. This is handy when editing. Without pressing
Enter, type into the Shell just

f
Then hold down the Alt key and press the / key. This key combination is
abbreviated Alt-/ . (On a Mac, that may give you a funny character: In that case
you need to hold down both the control key and the alt/option key when
pressing the ‘/’. This may hold in other places the Alt key is called for in
Windows.)

You should see f autocompleted to be

first

This is particularly useful if you have long identifiers! You can press Alt-/
several times if more than one identifier starts with the initial sequence of
characters you typed. If you press Alt-/ again you should see fred . Backspace
and edit so you have fi , and then and press Alt-/ again. You should not see
fred this time, since it does not start with fi .

1.6.1. Literals and Identifiers


«
Expressions like 27 or 'hello' are called literals, coming from the fact that they
literally mean exactly what they say. They are distinguished from variables,
whose value is not directly determined by their name.

The sequence of characters used to form a variable name (and names for other
Python entities later) is called an identifier. It identifies a Python variable or
other entity.

There are some restrictions on the character sequence that make up an identifier:

The characters must all be letters, digits, or underscores _ , and must start
with a letter. In particular, punctuation and blanks are not allowed.

There are some words that are reserved for special use in Python. You
may not use these words as your own identifiers. They are easy to
recognize in Idle, because they are automatically colored orange. For the
curious, you may read the full list:

False class finally is return


None continue for lambda try
True def from nonlocal while
and del global not with
as elif if or yield
assert else import pass
break except in raise

There are also identifiers that are automatically defined in Python, and that you
could redefine, but you probably should not unless you really know what you
are doing! When you start the editor, we will see how Idle uses color to help
you know what identifies are predefined.
Python is case sensitive: The identifiers last , LAST , and LaSt are all different.
Be sure to be consistent. Using the Alt-/ auto-completion shortcut in Idle helps
ensure you are consistent.

What is legal is distinct from what is conventional or good practice or


recommended. Meaningful names for variables are important for the humans
who are looking at programs, understanding them, and revising them. That
sometimes means you would like to use a name that is more than one word
long, like price at opening , but blanks are illegal! One poor option is just
leaving out the blanks, like priceatopening . Then it may be hard to figure out
where words split. Two practical options are

underscore separated: putting underscores (which are legal) in place of the


blanks, like price_at_opening .
using camel-case: omitting spaces and using all lowercase, except
capitalizing all words after the first, like priceAtOpening

Use the choice that fits your taste (or the taste or convention of the people you
are working with).
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Previous topic 1.7. Print Function, Part I


1.6. Variables and
Assignment In interactive use of the Python interpreter, you can type an expression
and immediately see the result of its evaluation. This is fine to test out
Next topic
syntax and maybe do simple calculator calculations. In a program run
1.8. Strings Part II
from a file like the first sample program, Python does not display
This Page expressions this way. If you want your program to display something,
you can give explicit instructions with the print function. Try in the Shell:
Show Source

Quick search x = 3
y = 5
print('The sum of', x, 'plus', y, 'is', x+y)

Go
The print function will prints as strings everything in a comma-separated
Enter search terms or a module, sequence of expressions, and it will separate the results with single
class or function name. blanks by default. Note that you can mix types: anything that is not
already a string is automatically converted to its string representation.

You can also use it with no parameters:


«
print()

to just advance to the next line.

Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Table Of Contents 1.8. Strings Part II


1.8. Strings Part II
1.8.1. Triple Quoted
String Literals
1.8.2. Escape Codes 1.8.1. Triple Quoted String Literals
Previous topic Strings delimited by one quote character, like ' , are required to lie within
1.7. Print Function, Part I a single Python line. It is sometimes convenient to have a multi-line
string, which can be delimited with triple quotes, ''' . Try typing the
Next topic
following. You will get continuation lines until the closing triple quotes.
1.9. The Idle Editor and
Execution Try in the Shell:

This Page sillyTest = '''Say,


"I'm in!"
Show Source This is line 3'''
print(sillyTest)
Quick search
The line structure is preserved in a multi-line string. As you can see, this
Go
also allows you to embed both single and double quote characters!

Enter search terms or a module,


class or function name.
1.8.2. Escape Codes
Continuing in the Shell with sillyTest , enter just

sillyTest

The answer looks strange! It indicates an alternate way to encode the


string internally in Python using escape codes. Escape codes are
embedded inside string literals and start with a backslash character \ .
They are used to embed characters that are either unprintable or have a
« special syntactic meaning to Python that you want to suppress. In this
example you see the most common ones:

Escape code Meaning


\\
\ (backslash)
\n
newline
\'
' (single quote)

The newline character indicates further text will appear on a new line
when printed. When you use a print function, you get the actual printed
meaning of the escape coded character.

Predict the result, and try in the Shell:

print('a\nb\n\nc')
Did you guess the right number of lines splitting in the right places?

Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Table Of Contents 1.9. The Idle Editor and Execution


1.9. The Idle Editor and
Execution
1.9.1. Loading a
Program in the Idle 1.9.1. Loading a Program in the Idle Editor,
Editor, and Running It
1.9.2. A Pre Python 3.3
and Running It
Idle Bug
1.9.3. The Classic First It is time to put longer collections of instructions together. That is most
Program easily done by creating a text file and running the Python interpreter on
1.9.4. Program the file. Idle simplifies that process.
Documentation String
1.9.5. Screen Layout First you can put an existing file into an Idle Edit Window. Click on the
Idle File menu and select Open . (Or as you see, you can use the
Previous topic
shortcut Ctrl-O . That means holding down the Ctrl key, and pressing
1.8. Strings Part II
the letter O for Open.) You should get a file selection dialog. You should
Next topic have the sample program madlib.py displayed in the list. Select it and
1.10. Input and Output open it. (If you do not see the program, then you either failed to
download the example programs in Your Python Folder and Python
This Page Examples, or you did not start Idle in the proper folder in Starting Idle.)
Show Source
Mac
Quick search In Windows it does not matter whether you go to the filemenu of the
Shell Window or an edit window, but it does make a difference on a
Go
Mac: If you use the Idle File menu when the Shell window is active,
your search will start in your Documents folder, but if some file
Enter search terms or a module, editing window is active instead, your search will start in its folder
class or function name. (generally the desired behavior).

You will see the source code again. Now run this program from inside of
Idle: Go to the Run menu of that Edit window, and select Run Module.
Notice the shortcut (F5).

If the Shell window does not automatically come to the foreground, select
it. You should see a line saying RESTART and then the start of the
execution of the Mad Lib program with the cursor waiting for your entry
after the first prompt. Finish executing the program. Be sure to type the
final requested Enter, so you get back to the interpreter prompt: >>>

Look at the editor window again. You should see that different parts of
the code have different colors. String literals are likely green. The
reserved words def are likely orange. Look at the last two lines, where
the identifier tellStory is black, and the identifier input is likely purple.
Only identifiers that are not predefined by Python are black. If you create
an identifier name, make sure Idle shows it in black.
1.9.2. A Pre Python 3.3 Idle Bug
Luckily this bug was fixed for Python 3.3, but if for some reason you are
using an older version and cannot update to the current version, read
on:

When you execute a program from the Idle Editor, the interpreter gives a
banner saying RESTART , meaning that all the things you defined in any
shell session so far are wiped clean and the program you are running
starts fresh. There is one egregious exception to that, that was still
present at least in the version of Idle for Python 3.1 in Windows. We will
try to demonstrate the bug. (A bug is an error in a program.)

Start running the Mad Lib program again by going to the Editor Window
containing madlib.py, and start running the program again, but do not
continue....

You should see a prompt for user input generated by the program. Ignore
this prompt and go back to the Edit Window and start the Mad Lib
program again.

If this bug is still present, you should see a difference in this restart: This
time after the RESTART banner and the interpreter prompt: >>> , which
looks innocent enough, but this program should show the program’s
prompt string for input, and with the bug, Idle does not show the
program’s prompt.

The problem only comes up because you interrupted the last execution
when user input was being waited for. The restart was not complete
here: The system is still looking for the pending user input from the last
execution.

The fix is simple: Make sure the Interpreter Window is the currently
selected window, and press return to terminate the lost user input. In
some circumstances, you may need to press return a second time.

After that the program should start up normally with its prompt.

Watch out for this behavior, and remember the fix.

1.9.3. The Classic First Program


Make sure you have Idle started in your Python directory (in Windows
with the provided Idle shortcut link), where you will store program files.

Warning: Do not start Idle from the Windows Start Menu!


If you just started Idle now, you may already have a blank Edit Window in
front of you. If not, open a new window by going to the File menu and
selecting New Window. This gives you a rather conventional text editing
window with the mouse available, ability to cut and paste, plus a few
special options for Python.

Type (or paste) the following into the editor window:

print('Hello world!')

Save the file with the menu sequence File Save, and then enter the file
name hello.py . Python program files should always be given a name
ending in ”.py”, and you must enter the .py extension explicitly .

Mac
Save updates a file using its current name. If you want to save a
copy to a different name, you must be careful: use File Save Copy
As, NOT File Save As. The latter saves a copy of the Shell history,
not the program you are editing! Sometimes you might want a
record of the Shell history, but this is not usually what you want.

If you look in the editor, you should see that your text is color coded. The
editor will color different parts of Python syntax in special colors.

Now that you have a complete, saved program, choose Run Run
Module. You should see the program run in the Python Shell window.

You just wrote and executed a program. Unlike when you use the shell,
this code is saved to a file in your Python folder. You can open and
execute the file any time you want. (In Idle, use File Open.)

To the interpreter, a program source file is a Python module. We will tend


to use the more general term: a program file is a module. Note the term
from the menu when running the program.

Distinguish program code from Shell text: It is easy to confuse the Shell
and the Edit windows. Make sure you keep them straight. The hello.py
program is just the line

print('Hello world!')

that you typed into the edit window and saved. When you ran the
program in Idle, you saw results in the Shell. First came the Restart
notice, the one-line output from the program saying hello, and a further
Shell prompt:

>>> ================================ RESTART ========


>>>
Hello world!
>>>
You could also have typed this single printing line directly in the Shell in
response to a Shell prompt. When you see >>> , you could enter the print
function and get the exchange between you and the Shell:

>>> print('Hello world')


Hello world!
>>>

Warning: The three lines above are not a program you could save in
a file and run. This is just an exchange in the Shell, with its >>>
prompts, individual line to execute and the response.

Again, just the single line, with no >>> ,

print('Hello world!')

entered into the Edit window forms a program you can save and run.

«
We will shortly get to more interesting many-statement programs, where
it is much more convenient to use the Edit window than the Shell!

The general assumption in this Tutorial will be that programs are run in
Idle and the Idle Shell is the Shell referred to. It will be explicitly stated
when you should run a program directly from the operating system.

In general it is fine to run our programs from a cmd console (Windows) or


terminal (Mac).

Warning: Running the text based example programs in Windows,


like birthday2.py, by selecting them to run from a file folder, will not
work well: The program ends and the window automatically closes
before you can see the final output.

On a Mac you get to explicitly close the terminal window created when
you run a Python program from the Finder.

1.9.4. Program Documentation String


The program above is self evident, and shows how short and direct a
program can be (unlike other languages such as Java). Still, right away,
get used to documenting a program. Python has a special feature: If the
beginning of a program is just a quoted string, that string is taken to be
the program’s documentation string. Open the example file hello2.py in
the Edit window:

'''A very simple program,


showing how short a Python program can be!
Authors: ___, ___
'''

print('Hello world!') #This is a stupid comment after the # mark

Most commonly, the initial documentation goes on for several lines, so a


multi-line string delimiter is used (the triple quotes). Just for
completeness of illustration in this program, another form of comment is
also shown, a comment that starts with the symbol # and extends to the
end of the line. The Python interpreter completely ignores this form of
comment. Such a comment should only be included for better human
understanding. Avoid making comments that do not really aid human
understanding. (Do what I say, not what I did above.) Good introductory
comment strings and appropriate names for the parts of your programs
make fewer # symbol comments needed.

Run the program and see the documentation and comment make no
difference in the result.

1.9.5. Screen Layout


Of course you can arrange the windows on your computer screen any
way that you like. A suggestion as you start to use the combination of
the editor to write, the shell to run, and the tutorial to follow along: Make
all three mostly visible your computer screen at once. Drag the editor
window to the upper left. Place the Shell window to the lower left, and
perhaps reduce its height a bit so there is not much overlap. If you are
looking at the web version of the tutorial on the screen, make it go top to
bottom on the right, but not overlap the Idle windows too much. The web
page rendering should generally adapt to the width pretty well. You can
always temporarily maximize the window. Before resizing the browser
window, it is good to look for an unusual phrase on your page, and
search for it after resizing, since resizing can totally mess up your
location in the web page.

There is an alternative to maximization for the Idle editor window: It you


want it to go top to bottom of the screen but not widen, you can toggle
that state with Alt-2 . Play with all this.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 24, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Table Of Contents 1.10. Input and Output


1.10. Input and Output
1.10.1. The input
Function
1.10.2. Print with 1.10.1. The input Function
Keyword Parameter
sep The hello program of The Classic First Program always does the same
1.10.3. Numbers and thing. This is not very interesting. Programs are only going to be reused
Strings of Digits if they can act on a variety of data. One way to get data is directly from
1.10.3.1. Exercise
the user. Modify the hello.py program as follows in the editor, and save it
for Addition
1.10.3.2. Exercise with File Save As....`, using the name hello_you.py .
for Quotients
1.10.4. String Format person = input('Enter your name: ')
Operation print('Hello', person)
1.10.4.1. Addition
Format Exercise Run the program. In the Shell you should see
1.10.4.2. Quotient
Format Exercise
Enter your name:
Previous topic
1.9. The Idle Editor and Follow the instruction (and press Enter). Make sure the typing cursor is in
Execution the Shell window, at the end of this line. After you type your response,
you can see that the program has taken in the line you typed. That is
Next topic what the built-in function input does: First it prints the string you give as
1.11. Defining Functions of
your Own
a parameter (in this case 'Enter your name: ' ), and then it waits for a
line to be typed in, and returns the string of characters you typed. In the
This Page hello_you.py program this value is assigned to the variable person, for

Show Source use later.

Quick search The parameter inside the parentheses after input is important. It is a
prompt, prompting you that keyboard input is expected at that point, and
hopefully indicating what is being requested. Without the prompt, the
Go
user would not know what was happening, and the computer would just
Enter search terms or a module, sit there waiting!
class or function name.
Open the example program, interview.py . Before running it (with any
made-up data), see if you can figure out what it will do:

'''Illustrate input and print.'''

applicant = input("Enter the applicant's name: ")


interviewer = input("Enter the interviewer's name: ")
time = input("Enter the appointment time: ")
print(interviewer, "will interview", applicant, "at", time)

The statements are executed in the order they appear in the text of the
program: sequentially. This is the simplest way for the execution of the
program to flow. You will see instructions later that alter that natural flow.
If we want to reload and modify the hello_you.py program to put an
exclamation point at the end, you could try:

person = input('Enter your name: ')


print('Hello', person, '!')

Run it and you see that it is not spaced right. There should be no space
after the person’s name, but the default behavior of the print function is
to have each field printed separated by a space. There are several ways
to fix this. You should know one. Think about it before going on to the
next section. Hint: [1]

[1] The + operation on strings adds no extra space.

1.10.2. Print with Keyword Parameter sep

One way to put punctuation but no space after the person in hello_you.py
is to use the plus operator, +. Another approach is to change the default
separator between fields in the print function. This will introduce a new
syntax feature, keyword parameters. The print function has a keyword
parameter named sep . If you leave it out of a call to print, as we have so
far, it is set equal to a space by default. If you add a final field, sep='' , in
the print function in hello_you.py , you get the following example file,
hello_you2.py:

'''Hello to you! Illustrates sep with empty string in print.


'''

person = input('Enter your name: ')


print('Hello ', person, '!', sep='')

Try the program.

Keyword paramaters must be listed at the end of the parameter list.

1.10.3. Numbers and Strings of Digits


Consider the following problem: Prompt the user for two numbers, and
then print out a sentence stating the sum. For instance if the user
entered 2 and 3, you would print ‘The sum of 2 and 3 is 5.’

You might imagine a solution like the example file addition1.py , shown
below. There is a problem. Can you figure it out before you try it? Hint:
[2]

'''Error in addition from input.'''

x = input("Enter a number: ")


y = input("Enter a second number: ")
print('The sum of ', x, ' and ', y, ' is ', x+y, '.', sep='')
#error

End up running it in any case.

We do not want string concatenation, but integer addition. We need


integer operands. Briefly mentioned in Whirlwind Introduction To Types
and Functions was the fact that we can use type names as functions to
convert types. One approach would be to do that. Further variable
names are also introduced in the example addition2.py file below to
emphasize the distinctions in types. Read and run:

'''Conversion of strings to int before addition'''

xString = input("Enter a number: ")


x = int(xString)
yString = input("Enter a second number: ")
y = int(yString)
print('The sum of ', x, ' and ', y, ' is ', x+y, '.', sep='')

Needing to convert string input to numbers is a common situation, both


with keyboard input and later in web pages. While the extra variables
above emphasized the steps, it is more concise to write as in the
variation in example file, addition3.py , doing the conversions to type int
immediately:

'''Two numeric inputs, with immediate conversion'''

x = int(input("Enter a number: "))


y = int(input("Enter a second number: "))
print('The sum of ', x, ' and ', y, ' is ', x+y, '.', sep='')

The simple programs so far have followed a basic programming pattern:


input-calculate-output. Get all the data first, calculate with it second, and
output the results last. The pattern sequence would be even clearer if
we explicitly create a named result variable in the middle, as in
addition4.py

'''Two numeric inputs, explicit sum'''

x = int(input("Enter an integer: "))


y = int(input("Enter another integer: "))
sum = x+y
print('The sum of ', x, ' and ', y, ' is ', sum, '.', sep='')

We will see more complicated patterns, which involve repetition, in the


future.

[2] The input function produces values of string type.

1.10.3.1. Exercise for Addition


Write a version, add3.py , that asks for three numbers, and lists all three,
and their sum, in similar format to addition4.py displayed above.

1.10.3.2. Exercise for Quotients


Write a program, quotient.py , that prompts the user for two integers, and
then prints them out in a sentence with an integer division problem like

The quotient of 14 and 3 is 4 with a remainder of 2

Review Division and Remainders if you forget the integer division or


remainder operator.

1.10.4. String Format Operation


In grade school quizzes a common convention is to use fill-in-the blanks.
For instance,

Hello _____!

and you can fill in the name of the person greeted, and combine given
text with a chosen insertion. We use this as an analogy: Python has a
similar construction, better called fill-in-the-braces. There is a particular
operation on strings called format , that makes substitutions into places
enclosed in braces. For instance the example file, hello_you3.py,
creates and prints the same string as in hello_you2.py from the previous
section:

'''Hello to you! Illustrates format with {} in print.


'''

person = input('Enter your name: ')


greeting = 'Hello, {}!'.format(person)
print(greeting)

There are several new ideas here!

First method calling syntax for objects is used. You will see this very
important modern syntax in more detail at the beginning of the next
chapter in Object Orientation. All data in Python are objects, including
strings. Objects have a special syntax for functions, called methods,
associated with the particular type of object. In particular str objects
have a method called format . The syntax for methods has the object
followed by a period followed by the method name, and further
parameters in parentheses.

object.methodname ( parameters )
In the example above, the object is the string 'Hello {}!' . The method is
named format . There is one further parameter, person .

The string for the format method has a special form, with braces
embedded. Places where braces are embedded are replaced by the
value of an expression taken from the parameter list for the format
method. There are many variations on the syntax between the braces. In
this case we use the syntax where the first (and only) location in the
string with braces has a substitution made from the first (and only)
parameter.

In the code above, this new string is assigned to the identifier greeting ,
and then the string is printed.

The identifier greeting was introduced to break the operations into a


clearer sequence of steps. However, since the value of greeting is only
referenced once, it can be eliminated with the more concise version:

person = input('Enter your name: ')


print('Hello {}!'.format(person))

Consider the interview program. Suppose we want to add a period at the


end of the sentence (with no space before it). One approach would be to
combine everything with plus signs. Another way is printing with
keyword sep='' . Another approach is with string formatting. Using our
grade school analogy, the idea is to fill in the blanks in

_____ will interview _____ at _____.

There are multiple places to substitute, and the format approach can be
« extended to multiple substitutions: Each place in the format string where
there is '{}' , the format operation will substitute the value of the next
parameter in the format parameter list.

Run the example file interview2.py , and check that the results from all
three methods match.

'''Compare print with concatenation and with format string.'''

applicant = input("Enter the applicant's name: ")


interviewer = input("Enter the interviewer's name: ")
time = input("Enter the appointment time: ")
print(interviewer + ' will interview ' + applicant + ' at ' +
time +'.')
print(interviewer, ' will interview ', applicant, ' at ', time,
'.', sep='')
print('{} will interview {} at {}.'.format(interviewer,
applicant, time))

Sometimes you want a single string, but not just for printing. You can
combine pieces with the + operator, but then all pieces must be strings
or explicitly converted to strings. An advantage of the format method is
that it will convert types to string automatically, like the print function.
Here is another variant of our addition sentence example, addition4a.py ,
using the format method.

'''Two numeric inputs, explicit sum'''

x = int(input("Enter an integer: "))


y = int(input("Enter another integer: "))
sum = x+y
sentence = 'The sum of {} and {} is {}.'.format(x, y, sum)
print(sentence)

Conversion to strings was not needed in interview2.py. (Everything


started out as a string.) In addition4a.py , however, the automatic
conversion of the integers to strings is useful.

So far there is no situation that requires a format string instead of using


other approaches. Sometimes a format string provides a shorter and
simpler expression. Except where specifically instructed in an exercise
for practice, use whatever approach to combining strings and data that
you like. There are many elaborations to the fields in braces to control
formatting. We will look at one later, String Formats for Float Precision,
where format strings are particularly useful.

A technical point: Since braces have special meaning in a format string,


there must be a special rule if you want braces to actually be included in
the final formatted string. The rule is to double the braces: '{{' and
'}}' . The example code formatBraces.py , shown below, makes setStr
refer to the string 'The set is {5,9}.' . The initial and final doubled
braces in the format string generate literal braces in the formatted string:

'''Illustrate braces in a formatted string.'''

a = 5
b = 9
setStr = 'The set is {{{}, {}}}.'.format(a, b)
print(setStr)

This kind of format string depends directly on the order of the parameters
to the format method. There is another approach with a dictionary, that
was used in the first sample program, madlib.py , and will be discussed
more in Dictionaries and String Formatting. The dictionary approach is
probably the best in many cases, but the count-based approach is an
easier start, particularly if the parameters are just used once, in order.

Optional elaboration with explicitly numbered entries


Imagine the format parameters numbered in order, starting from 0. In
this case 0, 1, and 2. The number of the parameter position may be
included inside the braces, so an alternative to the last line of
interview2.py is (added in example file interview3.py ):
print('{0} will interview {1} at {2}.'.format(interviewer,
applicant, time))

This is more verbose than the previous version, with no obvious


advantage. However, if you desire to use some of the parameters
more than once, then the approach with the numerical identification
with the parameters is useful. Every place the string includes '{0}' ,
the format operation will substitute the value of the initial parameter
in the list. Wherever '{1}' appears, the next format parameter will
be substituted....

Predict the results of the example file arith.py shown below, if you
enter 5 and 6. Then check yourself by running it. In this case the
numbers referring to the parameter positions are necessary. They
are both repeated and used out of order:

'''Fancier format string example with


parameter identification numbers
-- useful when some parameters are used several times.'''

x = int(input('Enter an integer: '))


y = int(input('Enter another integer: '))
formatStr = '{0} + {1} = {2}; {0} * {1} = {3}.'
equations = formatStr.format(x, y, x+y, x*y)
print(equations)

Try the program with other data.

Now that you have a few building blocks, you will see more exercises
where you need to start to do creative things. You are encouraged to go
back and reread Learning to Problem-Solve.

1.10.4.1. Addition Format Exercise


Write a version of Exercise for Addition, add3f.py , that uses the string
format method to construct the same final string as before.

1.10.4.2. Quotient Format Exercise


Write a version of the quotient problem in Exercise for Quotients,
quotientformat.py , that uses the string format method to construct the
same final string as before. Again be sure to give a full sentence stating
both the integer quotient and the remainder.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Table Of Contents 1.11. Defining Functions of your


1.11. Defining Functions of
your Own Own
1.11.1. Syntax Template
Typography
1.11.2. A First Function
Definition 1.11.1. Syntax Template Typography
1.11.3. Multiple
Function Definitions When new Python syntax is introduced, the usual approach will be to
1.11.3.1. Poem give both specific examples and general templates. In general templates
Function Exercise
for Python syntax the typeface indicates the the category of each part:
1.11.4. Function
Parameters
1.11.4.1. Birthday Typeface Meaning Example
Typewriter font sep=''
Function Exercise Text to be written verbatim
1.11.5. Multiple Emphasized A place where you can use an integerValue
Function Parameters arbitrary expression.
1.11.5.1. Quotient
Function Exercise Bold A place where you can use an variableName
1.11.6. Returned arbitrary identifier.
Function Values Normal text A description of what goes in that A digit, 0-9
1.11.6.1. Quotient position,without giving explicit
String Return syntax
Exercise
1.11.7. Two Roles: A more complete example of using this typography with several parts
Writer and Consumer
of Functions would be a description of an assignment statement:
1.11.8. Local Scope
1.11.9. Global variableName = someExpression
Constants
with an arbitrary identifier, the specific symbol = , and an expression.
Previous topic
1.10. Input and Output I try to make the parts that are not verbatim to be descriptive of the
expected use.
Next topic
1.12. Dictionaries We will use these conventions shortly in the discussion of function
syntax, and will continue to use the conventions throughout the tutorial.
This Page
Show Source

Quick search 1.11.2. A First Function Definition


If you know it is the birthday of a friend, Emily, you might tell those
Go gathered with you to sing “Happy Birthday to Emily”.
Enter search terms or a module, We can make Python display the song. Read, and run if you like, the
class or function name.
example program birthday1.py :

print("Happy Birthday to you!")


print("Happy Birthday to you!")
print("Happy Birthday, dear Emily.")
print("Happy Birthday to you!")
You would probably not repeat the whole song to let others know what to
sing. You would give a request to sing via a descriptive name like
“Happy Birthday to Emily”.

In Python we can also give a name like happyBirthdayEmily , and


associate the name with whole song by using a function definition. We
use the Python def keyword, short for define.

Read for now:

1 def happyBirthdayEmily(): #program does nothing as written


2 print("Happy Birthday to you!")
3 print("Happy Birthday to you!")
4 print("Happy Birthday, dear Emily.")
5 print("Happy Birthday to you!")

There are several parts of the syntax for a function definition to notice:

Line 1: The heading contains def , the name of the function, parentheses,
and finally a colon. A more general syntax is

def function_name ():

Lines 2-5: The remaining lines form the function body and are indented
by a consistent amount. (The exact amount is not important to the
interpreter, though 2 or 4 spaces are common conventions.)

The whole definition does just that: defines the meaning of the name
happyBirthdayEmily , but it does not do anything else yet - for example,
the definition itself does not make anything be printed yet. This is our
first example of altering the order of execution of statements from the
normal sequential order.

Note: The statements in the function definition are not executed as


Python first passes over the lines.

The code above is in example file birthday2.py . Load it in Idle and


execute it from there. Nothing should happen visibly. This is just like
defining a variable: Python just remembers the function definition for
future reference.

After Idle finished executing a program, however, its version of the Shell
remembers function definitions from the program.

In the Idle Shell (not the editor), enter

happyBirthdayEmily
The result probably surprises you! When you give the Shell an identifier,
it tells you its value. Above, without parentheses, it identifies the function
code as the value (and gives a location in memory of the code). Now try
the name in the Idle Shell with parentheses added:

happyBirthdayEmily()

The parentheses tell Python to execute the named function rather than
just refer to the function. Python goes back and looks up the definition,
and only then, executes the code inside the function definition. The term
for this action is a function call or function invocation.

Note: In the function call there is no def , but there is the function
name followed by parentheses.

function_name ()

In many cases we will use a feature of program execution in Idle: that


after program execution is completed, the Idle Shell still remembers
functions defined in the program. This is not true if you run a program by
selecting it directly in the operating system.

Look at the example program birthday3.py . See it just adds two more
lines, not indented. Can you guess what it does? Try it:

1 '''Function definition and invocation.'''


2
3 def happyBirthdayEmily():
4 print("Happy Birthday to you!")
5 print("Happy Birthday to you!")
6 print("Happy Birthday, dear Emily.")
7 print("Happy Birthday to you!")
8
9 happyBirthdayEmily()
10 happyBirthdayEmily()

The execution sequence is different from the textual sequence:

1. Lines 3-7: Python starts from the top, reading and remembering the
definition. The definition ends where the indentation ends. (The
code also shows a blank line there, but that is only for humans, to
emphasize the end of the definition.)
2. Line 9: this is not indented inside any definition, so the interpreter
executes it directly, calling happyBirthdayEmily() while
remembering where to return.
3. Lines 3-7: The code of the function is executed for the first time,
printing out the song.
4. End of line 9: Back from the function call. continue on.
5. Line 10: the function is called again while this location is
remembered.
6. Lines 3-7: The function is executed again, printing out the song
again.
7. End of line 10: Back from the function call, but at this point there is
nothing more in the program, and execution stops.

Functions alter execution order in several ways: by statements not being


executed as the definition is first read, and then when the function is
called during execution, jumping to the function code, and back at the
the end of the function execution.

If it also happens to be Andre’s birthday, we might define a function


happyBirthdayAndre , too. Think how to do that before going on ....

1.11.3. Multiple Function Definitions


Here is example program birthday4.py where we add a function
happyBirthdayAndre , and call them both. Guess what happens, and then
try it:

'''Function definitions and invocation.'''

def happyBirthdayEmily():
print("Happy Birthday to you!")
print("Happy Birthday to you!")
print("Happy Birthday, dear Emily.")
print("Happy Birthday to you!")

def happyBirthdayAndre():
print("Happy Birthday to you!")
print("Happy Birthday to you!")
print("Happy Birthday, dear Andre.")
print("Happy Birthday to you!")

happyBirthdayEmily()
happyBirthdayAndre()

Again, everything is definitions except the last two lines. They are the
only lines executed directly. The calls to the functions happen to be in
the same order as their definitions, but that is arbitrary. If the last two
lines were swapped, the order of operations would change. Do swap the
last two lines so they appear as below, and see what happens when you
execute the program:

happyBirthdayAndre()
happyBirthdayEmily()

Functions that you write can also call other functions you write. It is a
good convention to have the main action of a program be in a function
for easy reference. The example program birthday5.py has the two
Happy Birthday calls inside a final function, main . Do you see that this
version accomplishes the same thing as the last version? Run it. :

1 '''Function definitions and invocation.'''


2
3 def happyBirthdayEmily():
4 print("Happy Birthday to you!")
5 print("Happy Birthday to you!")
6 print("Happy Birthday, dear Emily.")
7 print("Happy Birthday to you!")
8
9 def happyBirthdayAndre():
10 print("Happy Birthday to you!")
11 print("Happy Birthday to you!")
12 print("Happy Birthday, dear Andre.")
13 print("Happy Birthday to you!")
14
15 def main():
16 happyBirthdayEmily()
17 happyBirthdayAndre()
18
19 main()

If we want the program to do anything automatically when it is runs, we


need one line outside of definitions! The final line is the only one directly
executed, and it calls the code in main , which in turn calls the code in the
other two functions.

Detailed order of execution:

1. Lines 3-17: Definitions are read and remembered


2. Line 19: The only statement outside definitions, is executed
directly. This location is remembered as main is executed.
3. Line 15: Start on main
4. Line 16. This location is remembered as execution jumps to
happyBirthdayEmily
5. Lines 3-7 are executed and Emily is sung to.
6. Return to the end of Line 16: Back from happyBirthdayEmily function
call
7. Line 17: Now happyBirthdayAndre is called as this location is
remembered.
8. Lines 9-13: Sing to Andre
9. Return to the end of line 17: Back from happyBirthdayAndre function
call, done with main
10. Return to the end of line 19: Back from main ; at the end of the
program

There is one practical difference from the previous version. After


execution, if we want to give another round of Happy Birthday to both
persons, we only need to enter one further call in the Shell to:
main()

As a simple example emphasizing the significance of a line being


indented, guess what the the example file order.py does, and run it to
check:

def f():
print('In function f')
print('When does this print?')
f()

Modify the file so the second print function is outdented like below. What
should happen now? Try it:

def f():
print('In function f')
print('When does this print?')
f()

The lines indented inside the function definition are remembered first,
and only executed when the function f is invoked at the end. The lines
outside any function definition (not indented) are executed in order of
appearance.

1.11.3.1. Poem Function Exercise


Write a program, poem.py , that defines a function that prints a short poem
or song verse. Give a meaningful name to the function. Have the
program end by calling the function three times, so the poem or verse is
repeated three times.

1.11.4. Function Parameters


As a young child, you probably heard Happy Birthday sung to a couple of
people, and then you could sing to a new person, say Maria, without
needing to hear the whole special version with Maria’s name in it word
for word. You had the power of abstraction. With examples like the
versions for Emily and Andre, you could figure out what change to make
it so the song could be sung to Maria!

Unfortunately, Python is not that smart. It needs explicit rules. If you


needed to explain explicitly to someone how Happy Birthday worked in
general, rather than just by example, you might say something like this:

First you have to be given a person’s name. Then you sing the song with
the person’s name inserted at the end of the third line.

Python works something like that, but with its own syntax. The term
“person’s name” serves as a stand-in for the actual data that will be
used, “Emily”, “Andre”, or “Maria”. This is just like the association with a
variable name in Python. “person’s name” is not a legal Python identifier,
so we will use just person as this stand-in.

The function definition indicates that the variable name person will be
used inside the function by inserting it between the parentheses of the
definition. Then in the body of the definition of the function, person is
used in place of the real data for any specific person’s name. Read and
then run example program birthday6.py :

1 '''Function with parameter.'''


2
3 def happyBirthday(person):
4 print("Happy Birthday to you!")
5 print("Happy Birthday to you!")
6 print("Happy Birthday, dear " + person + ".")
7 print("Happy Birthday to you!")
8
9 happyBirthday('Emily')
10 happyBirthday('Andre')

In the definition heading for happyBirthday , person is referred to as a


formal parameter. This variable name is a placeholder for the real name
of the person being sung to.

The last two lines of the program, again, are the only ones outside of
definitions, so they are the only ones executed directly. There is now an
actual name between the parentheses in the function calls. The value
between the parentheses here in the function call is referred to as an
argument or actual parameter of the function call. The argument
supplies the actual data to be used in the function execution. When the
call is made, Python does this by associating the formal parameter
name person with the actual parameter data, as in an assignment
statement. In the first call, this actual data is 'Emily' . We say the actual
parameter value is passed to the function. [1]

The execution in greater detail:

1. Lines 3-7: Definition remembered

2. Line 9: Call to happyBirthday , with actual parameter 'Emily' .

3. Line 3: 'Emily' is passed to the function, so person = 'Emily' .

4. Lines 4-7: The song is printed, with 'Emily' used as the value of
person in line 4: printing

Happy Birthday, dear Emily.

5. End of line 9 after returning from the function call


6. Line 10: Call to happyBirthday , this time with actual parameter
'Andre'

7. Line 3: 'Andre' is passed to the function, so person = 'Andre' .

8. Lines 4-7: The song is printed, with 'Andre' used as the value of
person in line 4: printing

Happy Birthday, dear Andre.

9. End of line 10 after returning from the function call, and the
program is over.

Note: Be sure you completely understand birthday6.py and the


sequence of execution! It illustrates extremely important ideas that
many people miss the first time!

It is essential to understand the difference between

1. Defining a function (lines 3-7) with the def heading including formal
parameter names, where the code is merely instructions to be
remembered, not acted on immediately.
2. Calling a function with actual parameter values to be substituted for
the formal parameters and have the function code actually run
when the instruction containing the call is run. Also note that the
function can be called multiple times with different expressions as
the actual parameters (line 9 and again in line 10).

The beauty of this system is that the same function definition can be
used for a call with a different actual parameter, and then have a
different effect. The value of the formal parameter person is used in the
third line of happyBirthday , to put in whatever actual parameter value was
given.

Note: This is the power of abstraction. It is one application of the


most important principal in programming. Rather than have a number
of separately coded parts with only slight variations, see where it is
appropriate to combine them using a function whose parameters refer
to the parts that are different in different situations. Then the code is
written to be simultaneously appropriate for the separate specific
situations, with the substitutions of the right parameter values.

You can go back to having a main function again, and everything works.
Run birthday7.py :

'''Function with parameter called in main'''


def happyBirthday(person):
print("Happy Birthday to you!")
print("Happy Birthday to you!")
print("Happy Birthday, dear " + person + ".")
print("Happy Birthday to you!")

def main():
happyBirthday('Emily')
happyBirthday('Andre')

main()

In birthday6.py , the function calls in lines 9 and 10 were outside any


function definition, so they did actually lead to immediate execution of
the function. In birthday7.py the calls to happyBirthday are inside
another function definition ( main ), so they are not actually run until the
function main is run (from the last line, outside any function).

See Birthday Function Exercise.

We can combine function parameters with user input, and have the
program be able to print Happy Birthday for anyone. Check out the main
method and run birthday_who.py :

'''User input supplies function parameter'''


1
2
def happyBirthday(person):
3
print("Happy Birthday to you!")
4
print("Happy Birthday to you!")
5
print("Happy Birthday, dear " + person + ".")
6
print("Happy Birthday to you!")
7
8
def main():
9
userName = input("Enter the Birthday person's name:
10
")
11
happyBirthday(userName)
12
13
main()

This last version illustrates several important ideas:

1. There are more than one way to get information into a function:
1. Have a value passed in through a parameter (from line 10 to
line 3).
2. Prompt the user, and obtain data from the keyboard (line 11).
2. It is a good idea to separate the internal processing of data from
the external input from the user by the use of distinct functions.
Here the user interaction is in main , and the data is manipulated in
happyBirthday .
3. In the first examples of actual parameters, we used literal values. In
general an actual parameter can be an expression. The
expression is evaluated before it is passed in the function call. One
of the simplest expressions is a plain variable name, which is
evaluated by replacing it with its associated value. Since it is only
the value of the actual parameter that is passed, not any variable
name, there is no need to have a variable name used in an actual
parameter match a formal parameter name. (Here we have the
value of userName in main becoming the value of person in
happyBirthday .)

Now that we have nested function calls, it is worth looking further at


tracebacks from execution errors. If I add a line to main in birthday7.py :

happyBirthday(2)

as in example file birthdayBad.py , and then run it, you get something
close to:

Traceback (most recent call last):


File “/hands-on/../examples/birthdayBad.py”, line 15, in
<module>
main()
File “/hands-on/../examples/birthdayBad.py”, line 13, in
main
happyBirthday(2)
File “/hands-on/../examples/birthdayBad.py”, line 6, in
happyBirthday
print(“Happy Birthday, dear ” + person + ”.”)
TypeError: Can’t convert ‘int’ object to str implicitly

Your file folder is probably different than /hands-on/examples. The last


three lines are most important, giving the line number where the error
was detected, the text of the line in question, and a description of what
problem was found. Often that is all you need to look at, but this
example illustrates that the genesis of the problem may be far away
from the line where the error was detected. Going further up the
traceback, you find the sequence of function calls that led to the line
where the error was detected. You can see that in main I call
happyBirthday with the bad parameter, 2.

1.11.4.1. Birthday Function Exercise


Make your own further change to birthday7.py and save it as
birthdayMany.py : Add a function call (but not another function definition),
so Maria gets a verse, in addition to Emily and Andre. Also print a blank
line between verses. (You may either do this by adding a print line to the
function definition, or by adding a print line between all calls to the
function.)

[1] I have given the explicit terminology “formal parameter” and “actual
parameter”. In various places you may see either of these terms
replaced by just “parameter” or maybe “argument”. In that case
you must determine from context which is being discussed: a
definition and formal parameter or a function call and an actual
parameter.

1.11.5. Multiple Function Parameters


A function can have more than one parameter in a parameter list
separated by commas. Here the example program addition5.py changes
example program addition4a.py , using a function to make it easy to
display many sum problems. Read and follow the code, and then run:

'''Display any number of sum problems with a function.


Handle keyboard input separately.
'''

def sumProblem(x, y):


sum = x + y
sentence = 'The sum of {} and {} is {}.'.format(x, y, sum)
print(sentence)

def main():
sumProblem(2, 3)
sumProblem(1234567890123, 535790269358)
a = int(input("Enter an integer: "))
b = int(input("Enter another integer: "))
sumProblem(a, b)

main()

The actual parameters in the function call are evaluated left to right, and
then these values are associated with the formal parameter names in
the function definition, also left to right. For example a function call with
actual parameters, f(actual1, actual2, actual3) , calling a function f with
definition heading:

def f(formal1, formal2, formal3):

acts approximately as if the first lines executed inside the called function
f were

formal1 = actual1
formal2 = actual2
formal3 = actual3

Functions provide extremely important functionality to programs, allowing


tasks to be defined once and performed repeatedly with different data. It
is essential to see the difference between the formal parameters used
to describe what is done inside the function definition (like x and y in the
definition of sumProblem) and the actual parameters (like 2 and 3 or
1234567890123 and 535790269358) which substitute for the formal
parameters when the function is actually executed. The main method
above uses three different sets of actual parameters in the three calls to
sumProblem.

1.11.5.1. Quotient Function Exercise


The example addition5.py is a modification of addition4a.py , putting the
arithmetic problem into a function and then calling the function several
times with different parameters. Similarly modify quotientformat.py from
Quotient Format Exercise and save it as quotientProb.py . You should
« create a function quotientProblem with numerical parameters. Like in all
the earlier versions, it should print a full sentence with input, quotient,
and remainder. The main method in the new program should test the
quotientProblem function on several sets of literal values, and also test
the function with input from the user.

1.11.6. Returned Function Values


You probably have used mathematical functions in algebra class, but
they all had calculated values associated with them. For instance if you
defined

f(x)=x2

then it follows that f(3) is 32, and f(3)+f(4) is 32 + 42

Function calls in expressions get replaced during evaluation by the value


of the function.

The corresponding definition and examples in Python would be the


following, taken from example program return1.py . Read and run:

'''A simple function returning a value, used in an expression'''

def f(x):
return x*x

print(f(3))
print(f(3) + f(4))

The new Python syntax is the return statement, with the word return
followed by an expression. Functions that return values can be used in
expressions, just like in math class. When an expression with a function
call is evaluated, the function call is effectively replaced temporarily by
its returned value. Inside the Python function, the value to be returned is
given by the expression in the return statement.

After the function f finishes executing from inside


print(f(3))

it is as if the statement temporarily became

print(9)

and similarly when executing

print(f(3) + f(4))

the interpreter first evaluates f(3) and effectively replaces the call by the
returned result, 9, as if the statement temporarily became

print(9 + f(4))

and then the interpreter evaluates f(4) and effectively replaces the call by
the returned result, 16, as if the statement temporarily became

print(9 + 16)

resulting finally in 25 being calculated and printed.

Python functions can return any type of data, not just numbers, and
there can be any number of statements executed before the return
statement. Read, follow, and run the example program return2.py :

'''A function returning a string and using a local


1
variable'''
2
3
def lastFirst(firstName, lastName):
4
separator = ', '
5
result = lastName + separator + firstName
6
return result
7
8
print(lastFirst('Benjamin', 'Franklin'))
9
print(lastFirst('Andrew', 'Harrington'))

The code above has a new feature, variables separator and result are
given a value inside the function, but separator and result are not
among the formal parameters. The assignments work as you would
expect here. More on this shortly, in Local Scope.

Details of the execution:

1. Lines 3-6: Remember the definition


2. Line 8: call the function, remembering where to return
3. Line 3: pass the parameters: firstName = 'Benjamin' ; lastName =
'Franklin'
4. Line 4: Assign the variable separator the value ', '
5. Line 5: Assign the variable result the value of lastName + separator
+ firstName which is 'Franklin' + ', ' + 'Benjamin' , which
evaluates to 'Franklin, Benjamin'
6. Line 6: Return 'Franklin, Benjamin'
7. Line 8: Use the value returned from the function call so the line
effectively becomes print('Franklin, Benjamin') , so print it.
8. Line 9: call the function with the new actual parameters,
remembering where to return
9. Line 3: pass the parameters: firstName = 'Andrew' ; lastName =
'Harrington'
10. Lines 4-6: ... calculate and return 'Harrington, Andrew'
11. Line 9: Use the value returned by the function and print
'Harrington, Andrew'

Compare return2.py and addition5.py , from the previous section. Both


use functions. Both print, but where the printing is done differs. The
function sumProblem prints directly inside the function and returns nothing
explicitly. On the other hand lastFirst does not print anything but
returns a string. The caller gets to decide what to do with the string, and
above it is printed in the main program.

Open addition5.py again, and introduce a common mistake. Change the


last line of the function main inserting print , so it says

print(sumProblem(a, b))

Then try running the program. The desired printing is actually done inside
the function sumProblem. You introduced a statement to print what
sumProblem returns. Although sumProblem returns nothing explicitly, Python
does make every function return something. If there is nothing explicitly
returned, the special value None is returned. You should see that in the
Shell output. This is a fairly common error.

Warning: If you see a ‘None’ is your printed output where you do not
expect it, it is likely that you have printed the return value of a function
that did not return anything explicitly!

In general functions should do a single thing. You can easily combine a


sequence of functions, and you have more flexibility in the combinations
if each does just one unified thing. The function sumProblem in
addition5.py does two things: It creates a sentence, and prints it. If that
is all you have, you are out of luck if you want to do something different
with the sentence string. A better way is to have a function that just
creates the sentence, and returns it for whatever further use you want.
Printing is one possibility, done in addition6.py :

'''Display a sum problems with a function returning a string,


not printing directly.
'''

def sumProblemString(x, y):


sum = x + y
return 'The sum of {} and {} is {}.'.format(x, y, sum)

def main():
print(sumProblemString(2, 3))
print(sumProblemString(1234567890123, 535790269358))
a = int(input("Enter an integer: "))
b = int(input("Enter another integer: "))
print(sumProblemString(a, b))

main()

1.11.6.1. Quotient String Return Exercise


Create quotientReturn.py by modifying quotientProb.py in Quotient
Function Exercise so that the program accomplishes the same thing, but
everywhere change the quotientProblem function into one called
quotientString that merely returns the string rather than printing the
string directly. Have the main function print the result of each call to the
quotientString function.

1.11.7. Two Roles: Writer and Consumer of


Functions
The remainder of this section covers finer points about functions that you
might skip on a first reading.

We are only doing tiny examples so far to get the basic idea of functions.
In much larger programs, functions are useful to manage complexity,
splitting things up into logically related, modest sized pieces.
Programmers are both writers of functions and consumers of the other
functions called inside their functions. It is useful to keep those two roles
separate:

The user of an already written function needs to know:

1. the name of the function


2. the order and meaning of parameters
3. what is returned or produced by the function

How this is accomplished is not relevant at this point. For instance, you
use the work of the Python development team, calling functions that are
built into the language. You need know the three facts about the
functions you call. You do not need to know exactly how the function
accomplishes its purpose.
On the other hand when you write a function you need to figure out
exactly how to accomplish your goal, name relevant variables, and write
your code, which brings us to the next section.

1.11.8. Local Scope


For the logic of writing functions, it is important that the writer of a
function knows the names of variables inside the function. On the other
hand, if you are only using a function, maybe written by someone
unknown to you, you should not care what names are given to values
used internally in the implementation of the function you are calling.
Python enforces this idea with local scope rules: Variable names
initialized and used inside one function are invisible to other functions.
Such variables are called local variables. For example, an elaboration of
the earlier program return2.py might have its lastFirst function with its
local variable separator , but it might also have another function that
defines a separator variable, maybe with a different value like '\n' . They
would not conflict. They would be independent. This avoids lots of
errors!

For example, the following code in the example program badScope.py


causes an execution error. Read it and run it, and see:

'''program causing an error with an undefined variable'''

def main():
x = 3
f()

def f():
print(x) # error: f does not know about the x defined in
main

main()

We will fix this error below. The execution error message mentions
“global name”. Names defined outside any function definition, at the
“top-level” of your program are called global. They are a special case.
They are discussed more in the next section.

If you do want local data from one function to go to another, define the
called function so it includes parameters! Read and compare and try the
program goodScope.py :

'''A change to badScope.py avoiding any error by passing a


parameter'''

def main():
x = 3
f(x)
def f(x):
print(x)

main()

With parameter passing, the parameter name x in the function f does


not need to match the name of the actual parameter in main . The
definition of f could just as well have been:

def f(whatever):
print(whatever)

1.11.9. Global Constants


If you define global variables (variables defined outside of any function
definition), they are visible inside all of your functions. They have global
scope. It is good programming practice to avoid defining global variables
and instead to put your variables inside functions and explicitly pass
them as parameters where needed. One common exception is
constants: A constant is a name that you give a fixed data value to, by
assigning a value to the name only in a single assignment statement.
You can then use the name of the fixed data value in expressions later.
A simple example program is constant.py :

'''Illustrate a global constant being used inside functions.'''

PI = 3.14159265358979 # global constant -- only place the value


of PI is set

def circleArea(radius):
return PI*radius*radius # use value of global constant PI

def circleCircumference(radius):
return 2*PI*radius # use value of global constant PI

def main():
print('circle area with radius 5:', circleArea(5))
print('circumference with radius 5:', circleCircumference(5))

main()

This example uses numbers with decimal points, discussed more in


Decimals, Floats, and Floating Point Arithmetic. By convention, names
for constants are all capital letters.

Issues with global variables do not come up if they are only used as
constants.

Function names defined at the top-level also have global scope. This is
what allows you to use one function you defined inside another function
you define, like calling circleArea from inside main .
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 24, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Table Of Contents 1.12. Dictionaries


1.12. Dictionaries
1.12.1. Definition and
Use of Dictionaries
1.12.1.1. Number 1.12.1. Definition and Use of Dictionaries
Dictionary
Exercise In common usage, a dictionary is a collection of words matched with their
1.12.2. Dictionaries and definitions. Given a word, you can look up its definition. Python has a
String Formatting built in dictionary type called dict which you can use to create
1.12.2.1. Mad Lib
dictionaries with arbitrary definitions for character strings. It can be used
Exercise
1.12.3. Dictionaries and for the common usage, as in a simple English-Spanish dictionary.
Python Variables
1.12.3.1. Quotient Look at the example program spanish1.py and run it.
String Dictionary
Exercise """A tiny English to Spanish dictionary is created,
using the Python dictionary type dict.
Previous topic Then the dictionary is used, briefly.
1.11. Defining Functions of """
your Own
spanish = dict()
Next topic spanish['hello'] = 'hola'
spanish['yes'] = 'si'
1.13. Loops and Sequences spanish['one'] = 'uno'
spanish['two'] = 'dos'
This Page spanish['three'] = 'tres'
spanish['red'] = 'rojo'
Show Source spanish['black'] = 'negro'
spanish['green'] = 'verde'
Quick search spanish['blue'] = 'azul'

print(spanish['two'])
print(spanish['red'])
Go

Enter search terms or a module, First an empty dictionary is created using dict() , and it is assigned the
class or function name. descriptive name spanish .

To refer to the definition for a word, you use the dictionary name, follow it
by the word inside square brackets. This notation can either be used on
the left-hand side of an assignment to make (or remake) a definition, or
it can be used in an expression (as in the print functions), where its
earlier definition is retrieved. For example,

spanish['hello'] = 'hola'

makes an entry in our spanish dictionary for 'hello' , with definition


'hola' .

print(spanish['red'])

retrieves the definition for 'red' , which is 'rojo' .


Since the Spanish dictionary is defined at the top-level, the variable
name spanish is still defined after the program runs: After running the
program, use spanish in the Shell to check out the translations of some
more words, other than 'two' and 'red' .

Creating the dictionary is a well defined and quite different activity from
the use of the dictionary at the end of the code, so we can use a
functions to encapsulate the task of creating the dictionary, as in the
example program spanish2.py , which gives the same result:

"""A tiny English to Spanish dictionary is created,


using the Python dictionary type dict.
Then the dictionary is used, briefly.
"""

def createDictionary():
'''Returns a tiny Spanish dictionary'''
spanish = dict()
spanish['hello'] = 'hola'
spanish['yes'] = 'si'
spanish['one'] = 'uno'
spanish['two'] = 'dos'
spanish['three'] = 'tres'
spanish['red'] = 'rojo'
spanish['black'] = 'negro'
spanish['green'] = 'verde'
spanish['blue'] = 'azul'
return spanish

def main():
dictionary = createDictionary()
print(dictionary['two'])
print(dictionary['red'])

main()

This code illustrates several things about functions.

First, like whole files, functions can have a documentation string


immediately after the definition heading. It is a good idea to
document the return value!
The dictionary that is created is returned, but the local variable
name in the function, spanish , is lost when the function terminates.
To remember the dictionary returned to main , it needs a name. The
name does not have to match the name used in createDictionary .
The name dictionary is descriptive.

We could also use the dictionary more extensively. The example


program spanish2a.py is the same as above except it has the following
main method:

def main():
dictionary = createDictionary()
print('Count in Spanish: ' + dictionary['one'] + ', ' +
dictionary['two'] + ', ' +dictionary['three'] + ',
...')
print('Spanish colors: ' + dictionary['red'] + ', ' +
dictionary['blue'] + ', ' +dictionary['green'] + ',
...')

Try it, and check that it makes sense.

Python dictionaries are actually more general than the common use of
dictionaries. They do not have to associate words and their string
definitions. They can associate many types of objects with some
arbitrary object. The more general Python terminology for word and
definition are key and value. Given a key, you can look up the
corresponding value. The only restriction on the key is that it be an
immutable type. This means that a value of the key’s type cannot be
changed internally after it is initially created. Strings and numbers are
immutable. A dictionary is mutable: its value can be changed internally.
(You can add new definitions to it!) We will see more mutable and
immutable types later and explore more of the internal workings of data
types.

1.12.1.1. Number Dictionary Exercise


Write a tiny Python program numDict.py that makes a dictionary whose
keys are the words ‘one’, ‘two’, ‘three’, and ‘four’, and whose
corresponding values are the numerical equivalents, 1, 2, 3, and 4 (ints,
not strings). Include code to test the resulting dictionary by referencing
several of the definitions and printing the results.

1.12.2. Dictionaries and String Formatting


At the end of the main function in spanish2a.py from the last section, two
strings are constructed and printed. The expressions for the two strings
include a sequence of literal strings concatenated with interspersed
values from a dictionary. There is a much neater, more readable way to
generate these strings. We will develop this in several steps. The first
string could be constructed and printed as follows:

numberFormat = 'Count in Spanish: {one}, {two}, {three}, ...'


withSubstitutions = numberFormat.format(one='uno', two='dos',
three='tres')
print(withSubstitutions)

There are several new ideas here!. We are using an alternate form of
format string and format method parameters from those described in
String Format Operation.

Note the form of the string assigned the name numberFormat : It has the
English words for numbers in braces where we want the Spanish
definitions substituted. (This is unlike in String Format Operation, where
we had empty braces or a numerical index inside.)

As in String Format Operation, the second line uses method calling


syntax. A reminder of the syntax for methods:

object.methodname ( parameters )

has the object followed by a period followed by the method name, and
further parameters in parentheses.

In the example above, the object is the string called numberFormat . The
method is named format . The parameters in this case are all keyword
parameters. You have already seen keyword parameters sep and end
used in print function calls. In this particular application, the keywords
are chosen to include all the words that appear enclosed in braces in the
numberFormat string.

When the string numberFormat has the format method applied to it with the
given keyword parameters, a new string is created with substitutions into
the places enclosed in braces. The substitutions are just the values
given by the keyword parameters. Hence the printed result is

Count in Spanish: uno, dos, tres, ...

Now we go one step further: The keyword parameters associate the


keyword names with the values after the equal signs. The dictionary
from spanish2a.py includes exactly the same associations. There is a
special notation allowing such a dictionary to supply keyword
parameters. Assuming dictionary is the Spanish dictionary from
spanish2a.py , the method call

numberFormat.format(one='uno', two='dos', three='tres')

returns the same string as

numberFormat.format(**dictionary)

The special syntax ** before the dictionary indicates that the dictionary is
not to be treated as a single actual parameter. Instead keyword
arguments for all the entries in the dictionary effectively appear in its
place.

Below is a substitute for the main method in spanish2a.py . The whole


revised program is in example program spanish3.py :
«
def main():
dictionary = createDictionary()
numberFormat = "Count in Spanish: {one}, {two}, {three}, ..."
withSubstitutions = numberFormat.format(**dictionary)
print(withSubstitutions)
print("Spanish colors: {red}, {blue}, {green},
...".format(**dictionary))

In this main function the string with the numbers is constructed in steps
as discussed above. The printing of the string with the Spanish colors is
coded more concisely. There are not named variables for the format
string or the resulting formatted string. You are free to use either coding
approach.

In general, use this syntax for the string format method with a dictionary,
returning a new formatted string:

formatString .format(** aDictionary )

where the format string contains dictionary keys in braces where you
want the dictionary values substituted. The dictionary key names must
follow the rules for legal identifiers.

At this point we have discussed in some detail everything that went into
the first sample program, madlib.py , in A Sample Program, Explained!
This is certainly the most substantial program so far.

Look at madlib.py again, see how we have used most of the ideas so far.
If you want more description, you might look at section A Sample
Program, Explained again (or for the first time): it should make much
more sense now.

We will use madlib.py as a basis for more substantial modifications in


structure in The Revised Mad Lib Program.

1.12.2.1. Mad Lib Exercise


To confirm your better understanding of madlib.py , load it in the editor,
rename it as myMadlib.py , and modify it to have a less lame story, with
more and different entries in the dictionary. Make sure addPick is called
for each key in your format string. Test your version.

1.12.3. Dictionaries and Python Variables


Dictionaries are central to the implementation of Python. Each variable
identifier is associated with a particular value. These relationships are
stored in dictionaries in Python, and these dictionaries are accessible to
the user: You can use the function call locals() to return a dictionary
containing all the current local variables names as keys and all their
values as the corresponding dictionary values. This dictionary can be
used with the string format method, so you can embed local variable
names in a format string and use them very easily!

For example, run the example program arithDict.py

'''Fancier format string example, with locals().'''

x = 20
y = 30
sum = x + y
prod = x * y
formatStr = '{x} + {y} = {sum}; {x} * {y} = {prod}.'
equations = formatStr.format(**locals())
print(equations)

Note the variable names inside braces in formatStr, and the dictionary
reference used as the format parameter is **locals() .

A string like formatStr is probably the most readable way to code the
creation of a string from a collection of literal strings and program
values. The ending part of the syntax, .format(**locals()) , may appear
a bit strange, but it is very useful! We will use this notation extensively. It
clearly indicate how values are embedded into the format string, and
avoids having a long list of parameters to format .

The example program hello_you4.py does the same thing as the earlier
hello_you versions, but with a dictionary reference:

'''Hello to you! Illustrates locals() for formating in print.


'''

person = input('Enter your name: ')


greeting = 'Hello, {person}!'.format(**locals())
print(greeting)

1.12.3.1. Quotient String Dictionary Exercise


Create quotientDict.py by modifying quotientReturn.py in Quotient String
Return Exercise so that the quotientString function accomplishes the
same thing, but change the format method call to use the dictionary
**locals() as parameter, and put local variable names inside the
braces. If you use meaningful names for the variables, the format string
should be particularly easy to understand.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Table Of Contents 1.13. Loops and Sequences


1.13. Loops and Sequences
1.13.1. Updating Modern computers can do millions or even billions of instructions a
Variables second. With the techniques discussed so far, it would be hard to get a
1.13.2. The list Type
program that would run by itself for more than a fraction of a second. [1]
1.13.3. The range
Function, Part 1
1.13.4. Basic for Loops Practically, we cannot write millions of instructions to keep the computer
1.13.4.1. Pattern busy. To keep a computer doing useful work we need repetition, looping
Loop Exercise back over the same block of code again and again. There are two
1.13.4.2. Triple Python statement types to do that: the simpler for loops, which we take
Exercise
up shortly, and while loops, which we take up later, in While Statements.
1.13.5. Simple Repeat
Loop
1.13.6. Successive Two preliminaries:
Modification Loops
1.13.7. Accumulation 1. The value of already defined variables can be updated. This will be
Loops particularly important in loops. To prepare for that we first follow
1.13.7.1. Play how variables can be updated in an even simpler, sequential
Computer sumList situation.
Exercise
2. Sequence types are used in for loops. We will look at a basic
1.13.7.2. Test
sumList Exercise sequence type: list .
1.13.7.3. Join All
Exercise Then we put this all together. This is a long section. Go slowly and
1.13.8. More Playing carefully.
Computer
1.13.8.1. Play [1] It is possible with function recursion, but we will avoid that
Computer Odd
advanced topic in this introduction.
Loop Exercise
1.13.8.2. Play
Computer Error
Exercise
1.13.1. Updating Variables
1.13.8.3. Play
Computer The programs so far have defined and used variables, but other than in
Functions early shell examples we have not changed the value of existing
Exercise variables. For now consider a particularly simple example, just chosen
1.13.9. The print as an illustration, in the example file updateVar.py :
function keyword end

Previous topic
1 x = 3 # simple sequential code
1.12. Dictionaries 2 y = x + 2 # updating two variables
3 y = 2*y
Next topic 4 x = y - x
1.14. Decimals, Floats, and 5 print(x, y)
Floating Point Arithmetic

This Page Can you predict the result? Run the program and check. Particularly if
Show Source you did not guess right, it is important to understand what happens, one
step at a time. That means keeping track of what changes to variables
Quick search are made by each statement.

In the table below, statements are referred to by the numbers labeling


Go
the lines in the code above. We can track the state of each variable after
Enter search terms or a module, each line is executed. A dash is shown where a variable is not defined.
class or function name. For instance after line 1 is executed, a value is given to x, but y is still
undefined. Then y gets a value in line 2. The comment on the right
summarizes what is happening. Since x has the value 3 when line 2
starts, x+2 is the same as 3+2. In line three we use the fact that the right
side of an assignment statement uses the values of variables when the
line starts executing (what is left after the previous line of the table
executed), but the assignment to the variable y on the left causes a
change to y, and hence the updated value of y, 10, is shown in the table.
Line 4 then changes x, using the latest value of y (10, not the initial
value 5!). The result from line 5 confirms the values of x and y.

Line x y Comment
1 3 -
2 3 5 5=3+2, using the value of x from the previous line
3 3 10 10=2*5 on the right, use the value of y from the previous
line
4 7 10 7=10-3 on the right, use the value of x and y from the
previous line
5 7 10 print: 7 10

The order of execution will always be the order of the lines in the table. In
this simple sequential code, that also follows the textual order of the
program. Following each line of execution of a program in the proper
order of execution, carefully, keeping track of the current values of
variables, will be called playing computer. A table like the one above is
an organized way to keep track.

1.13.2. The list Type


Lists are ordered sequences of arbitrary data. Lists are the first kind of
data discussed so far that are mutable: the length of the sequence can
be changed and elements substituted. We will delay the discussion of
changes to lists until a further introduction to objects. Lists can be written
explicitly. Read the following examples

['red', 'green', 'blue']


[1, 3, 5, 7, 9, 11]
['silly', 57, 'mixed', -23, 'example']
[] # the empty list

The basic format is a square-bracket-enclosed, comma-separated list of


arbitrary data.

1.13.3. The range Function, Part 1


There is a built-in function range , that can be used to automatically
generate regular arithmetic sequences. Try the following in the Shell:

list(range(4))
list(range(10))

The general pattern for use is

range( sizeOfSequence )

This syntax will generate the integers, one at a time, as needed [2]. If you
want to see all the results at once as a list, you can convert to a list as
in the examples above. The resulting sequence starts at 0 and ends
before the parameter. We will see there are good reasons to start from 0
in Python. One important property of sequences generated by range(n)
is that the total number of elements is n . The sequence omits the
number n itself, but includes 0 instead.

With more parameters, the range function can be used to generate a


much wider variety of sequences. The elaborations are discussed in
Random Colors and The Most General range Function.

[2] In computer jargon, producing values of a sequence only as


needed is called lazy evaluation.

1.13.4. Basic for Loops


Try the following in the Shell. You get a sequence of continuation lines
before the Shell responds. Be sure to indent the second and third lines.
(In an edit window the indentation is automatic). Be sure to enter
another empty line. (Just press Enter .) at the end to get the Shell to
respond. :

1 for count in [1, 2, 3]:


2 print(count)
3 print('Yes' * count)

This is a for loop. It has the heading starting with for , followed by a
variable name ( count in this case), the word in , some sequence, and a
final colon. As with function definitions and other heading lines, the colon
at the end of the line indicates that a consistently indented block of
statements follows to complete the for loop.

for item in sequence :


indented statements to repeat; may use item

The block of lines is repeated once for each element of the sequence, so
in this example the two lines in the indented block are repeated three
times. Furthermore the variable in the heading ( count here) may be used
in the block, and each time through it takes on the next value in the
sequence, so the first time through the loop count is 1, then 2, and finally
3. Look again at the output and see that it matches this sequence. A
more detailed sequence is given, playing computer, in the table:

Line count comment


1 1 start with the first element of the list
2 1 print 1
3 1 ‘yes’ * 1 is ‘yes’; print yes
1 2 change count to the next element in the list
2 2 print 2
3 2 ‘yes’ * 2 is ‘yesyes’; print yesyes;
1 3 change count to the next element in the list
2 3 print 3
3 3 ‘yes’ * 3 is ‘yesyesyes’; print yesyesyes; done with list

When executing step by step, note that the for loop heading serves two
purposes:

Each time the heading line executes, it implicitly assigns a new


value to the variable name you use in place of item.
After each execution of the heading line, the statements in the
indented block are executed, generally making use of the the new
value for the variable assigned in the heading.

Note: When playing computer with a loop, the same line numbers
can reappear over and over, because the for loop heading line and
the indented body under it are each executed repeatedly, and each
time it is executed must be listed separately, in time sequence!

When you used the Shell to enter a loop, there was a reason that the
interpreter waited to respond until after you entered an empty line: The
interpreter did not know how long the loop block was going to be! The
empty line is a signal to the interpreter that you are done with the loop
block.

Look at the following example program for123.py , and run it.

for count in [1, 2, 3]:


print(count)
print('Yes' * count)
print('Done counting.')
for color in ['red', 'blue', 'green']:
print(color)

In a file, where the interpreter does not need to respond immediately, the
blank line is not necessary. Instead, as with a function definition or any
other format with an indented block, you indicate being past the indented
block by dedenting to line up with the for -loop heading. Hence in the
code above, “Done Counting.” is printed once after the first loop
completes all its repetitions. Execution ends with another simple loop.

As with the indented block in a function, it is important to get the


indentation right. Alter the code above, so the fourth line is indented:

for count in [1, 2, 3]:


print(count)
print('Yes' * count)
print('Done counting.') # changed so indented
for color in ['red', 'blue', 'green']:
print(color)

Predict the change, and run the code again to test.

Loops are one of the most important features in programming. While the
for loop syntax is pretty simple, using them creatively to solve problems
(rather than just look at a demonstration) is among the biggest
challenges for many learners at an introductory level. One way to
simplify the learning curve is to classify common situations and patterns,
and give them names. One of the simplest patterns is illustrated in all of
the for loop examples so far, a simple for-each loop: For each element
of the sequence, do the same sort of thing with it. Stated as mor
Pythonic pseudocode:

for item in sequence :


do something with the current item

(It would be even more like English if for were replace by for each , but
the shorter version is the one used by Python.)

In the for loop examples above, something is printed that is related to


each item in the list. Printing is certainly one form of “do something”, but
the possibilities for “do something” are completely general!

We can use a for-each loop to revise our first example. Recall the code
from madlib.py:

addPick('animal', userPicks)
addPick('food', userPicks)
addPick('city', userPicks)

Each line is doing exactly the same thing, except varying the string used
as the cue, while repeating the rest of the line. This is the for-each
pattern, but we need to list the sequence that the cues come from. Read
the alternative:

for cue in ['animal', 'food', 'city']: # heading


addPick(cue, userPicks) # body

Seeing this feature requires the ability to abstract the general pattern
from the group fo examples. This is essential for using loops effectively.

If you wish to see or run the whole program with this small modification,
see the example madlibloop.py . A common naming convention is used in
the program: Each element in the list is a cue , while the list with all the
elements is named with the plural cues . In later situations I make a list
name be the plural of the variable name used for an individual item of
the list.

Note the logic of the transformation between the two program versions:
The alternative pieces of data are collected in the list in the for loop
heading. A single variable name (here I chose cue ) is used in the
heading as a placeholder to refer to the current choice being handled,
and the body refers to this variable cue in place of the explicit data
values included each time in the original no-loop version.

It is important to understand the sequence of operations, how execution


goes back and forth between the heading and the body. Here are the
details:

1. heading first time: variable cue is set to the first element of the
sequence, 'animal'
2. body first time: since cue is now 'animal' , effectively execute
addPick('animal', userPicks) (Skip the details of the function call in
this outline.)
3. heading second time: variable cue is set to the next element of the
sequence, 'food'
4. body second time: since cue is now 'food' , effectively execute
addPick('food', userPicks)
5. heading third time: variable cue is set to the next (last) element of
the sequence, 'city'
6. body third time: since cue is now 'city' , effectively execute
addPick('city', userPicks)
7. heading done: Since there are no more elements in the sequence,
the entire for loop is done and execution would continue with the
statement after it (not indented).

In this example the data values are just a few given literals, and there is
only one line in the repeated pattern. Hence the use of a for loop is not
a big deal, but it makes a simple example! This looping construction
would be much handier if you were to modify the original mad lib
example, and had a story with many more cues. Also this revision will
allow for further improvements in The Revised Mad Lib Program, after
we introduce more about string manipulation.
1.13.4.1. Pattern Loop Exercise
Write a two-line for-each loop in a file types2.py containing a call to the
type function that will have the same effect as this code in example file
types1.py :

print(2, type(2))
print(3.5, type(3.5))
print([], type([]))
print(True, type(True))
print(None, type(None))

Execute both versions to check yourself. Hint1: [3] Hint2: [4]

1.13.4.2. Triple Exercise


Complete the following function. This starting code is in tripleStub.py .
Save it to the new name triple.py . Note the way an example is given in
the documentation string. It simulates the use of the function in the
Shell. This is a common convention:

def tripleAll(nums):
''' print triple each of the numbers in the list nums.
>>> tripleAll([2, 4, 1, 5])
6
12
3
15
>>> tripleAll([-6])
-18
'''

[3] The elements of the list in the for loop heading are not all of the
same type.
[4] You need to use the loop variable twice in the loop body.

1.13.5. Simple Repeat Loop


The examples above all used the value of the variable in the for loop
heading. An even simpler for loop usage is when you just want to
repeat the exact same thing a specific number of times. In that case only
the length of the sequence, not the individual elements are important.
We have already seen that the range function provides an ease way to
produce a sequence with a specified number of elements. Read and run
the example program repeat1.py :

''' A simple repeat loop'''

for i in range(10):
print('Hello')
In this situation, the variable i is not used inside the body of the for-loop.

The user could choose the number of times to repeat. Read and run the
example program repeat2.py :

'''The number of repetitions is specified by the user.'''

n = int(input('Enter the number of times to repeat: '))


for i in range(n):
print('This is repetitious!')

1.13.6. Successive Modification Loops


Suppose I have a list of items called items , and I want to print out each
item and number them successively. For instance if items is ['red',
'orange', 'yellow', 'green'] , I would like to see the output:

1 red
2 orange
3 yellow
4 green

Read about the following thought process for developing this:

If I allow myself to omit the numbers, it is easy: For any item in the list, I
can process it with

print(item)

and I just go through the list and do it for each one. (Copy and run if you
like.)

items = ['red', 'orange', 'yellow', 'green']


for item in items:
print(item)

Clearly the more elaborate version with numbers has a pattern with some
consistency, each line is at least in the form:

number item

but the number changes each time, and the numbers do not come
straight from the list of items.

A variable can change, so it makes sense to have a variable number , so


we have the potential to make it change correctly. We could easily get it
right the first time, and then repeat the same number. Read and run the
example program numberEntries1.py :
'''In this version number does not change.'''

items = ['red', 'orange', 'yellow', 'green']


number = 1
for item in items:
print(number, item)

Of course this is still not completely correct, since the idea was to count.
After the first time number is printed, it needs to be changed to 2, to be
right the next time through the loop, as in the following code: Read and
run the example program numberEntries2.py :

'''prints poorly numbered entries from the list'''

items = ['red', 'orange', 'yellow', 'green']


number = 1
for item in items:
print(number, item)
number = 2 # will change to 2 after printing 1

This is closer, but still not completely correct, since we never get to 3!
We need a way to change the value of number that will work each time
through the loop. The pattern of counting is simple, so simple in fact that
you probably do not think consciously about how you go from one
number to the next: You can describe the pattern by saying each
successive number is one more than the previous number. We need to
be able to change number so it is one more than it was before. That is the
additional idea we need! Change the last line of the loop body to get the
example program numberEntries3.py. See the addition and run it:

1 items = ['red', 'orange', 'yellow', 'green']


2 number = 1
3 for item in items: # print numbered entries
4 print(number, item)
5 number = number + 1 # crucial added line

It is important to understand the step-by-step changes during execution.


Below is another table showing the results of playing computer. The line
numbers are much more important here to keep track of the flow of
control, because of the jumping around at the end of the loop.

Again note that the program line numbers in the Line column of the
playing computer table are not all sequential, because the for loop
heading line and the indented body under it are each executed
repeatedly.

For compactness, the variable items does not get its own column, since it
always has the value shown in the comment in line 1:

line item number comment


1 - - set items to [‘red’, ‘orange’,’yellow’, ‘green’]
2 - 1
3 ‘red’ 1 start with item as first in sequence
4 ‘red’ 1 print: 1 red
5 ‘red’ 2 2 = 1+1
3 ‘orange’ 2 on to the next element in sequence
4 ‘orange’ 2 print 2 orange
5 ‘orange’ 3 3=2+1
3 ‘yellow’ 3 on to the next element in sequence
4 ‘yellow’ 3 print 3 yellow
5 ‘yellow’ 4 4=3+1
3 ‘green’ 4 on to the last element in sequence
4 ‘green’ 4 print 4 green
5 ‘green’ 5 5=4+1
3 ‘green’ 5 sequence done, end loop and code

The final value of number is never used, but that is OK. What we want
gets printed.

Go through carefully and be sure you understand the meaning of each


entry in the table, and the reason for the sequencing and the reason for
the exact position of each entry in each step where it changes! In
particular see how and why the line number for each successive row is
not always one more than the previous row. In particular, see how the
same sequence of numbered lines may be repeated in multiple places in
the table. Without this understanding you will not be able to play
computer yourself and really understand loops.

This short example illustrates a lot of ideas important to loops:

Loops may contain several variables.


One way a variable can change is by being the variable in a for
loop heading, that automatically goes through the values in the for
loop list.
Another way to have variables change in a loop is to have an
explicit statement that changes the variable inside the loop,
causing successive modifications.

There is a general pattern to loops with successive modification of a


variable like number above:

1. The variables to be modified need initial values before the loop (line
1 in the example above).
2. The loop heading causes the repetition. In a for-loop, the number of
repetitions is the same as the size of the list.
3. The body of the loop generally “does something” (like print above in
line 4) that you want done repeatedly.
4. There is code inside the body of the loop to set up for the next time
through the loop, where the variable which needs to change gets
transformed to its next value (line 5 in the example above).

This information can be put in a code outline:

Initialize variables to be modified


Loop heading controlling the repetition:
Do the desired action with the current variables
Modify variables to be ready for the action the next time

If you compare this pattern to the for-each and simple repeat loops in
Basic for Loops, you see that the examples there were simpler. There
was no explicit variable modification needed to prepare for the next time
though the loop. We will refer to the latest, more general pattern as a
successive modification loop.

Functions are handy for encapsulating an idea for use and reuse in a
program, and also for testing. We can write a function to number a list,
and easily test it with different data. Read and run the example program
numberEntries4.py :

''' use a function to number the entries in any list'''

def numberList(items):
'''Print each item in a list items, numbered in order.'''
number = 1
for item in items:
print(number, item)
number = number + 1

def main():
numberList(['red', 'orange', 'yellow', 'green'])
print()
numberList(['apples', 'pears', 'bananas'])

main()

Make sure you can follow the whole sequence, step by step! This
program has the most complicated flow of control so far, changing both
for function calls and loops.

1. Execution start with the very last line, since the previous lines are
definitions

2. Then main starts executing.

3. The first call to numberList effectively sets the formal parameter

items = ['red', 'orange', 'yellow', 'green']

and the function executes just like the flow followed in


numberEntries3.py . This time, however, execution returns to main.

4. An empty line is printed in the second line of main.


5. The second call to numberList has a different actual parameter
['apples', 'pears', 'bananas'] , so this effectively sets the formal
parameter this time

items = ['apples', 'pears', 'bananas']

and the function executes in a similar pattern as in


numberEntries3.py , but with different data and one less time
through the loop.

6. Execution returns to main, but there is nothing more to do.

1.13.7. Accumulation Loops


Suppose you want to add up all the numbers in a list, nums . Let us plan
this as a function from the beginning, so read the code below. We can
start with:

def sumList(nums):
'''Return the sum of the numbers in nums.'''

If you do not see what to do right away, a useful thing to do is write down
a concrete case, and think how you would solve it, in complete detail. If
nums is [2, 6, 3, 8] , you would likely calculate:

2 + 6 is 8
8 + 3 is 11
11 + 8 is 19
19 is the answer to be returned.

Since the list may be arbitrarily long, you need a loop. Hence you must
find a pattern so that you can keep reusing the same statements in the
loop. Obviously you are using each number in the sequence in order.
You also generate a sum in each step, which you reuse in the next step.
The pattern is different, however, in the first line, 2+6 is 8: there is no
previous sum, and you use two elements from the list. The 2 is not
added to a previous sum.

Although it is not the shortest way to do the calculation by hand, 2 is a


sum of 0 + 2: We can make the pattern consistent and calculate:

start with a sum of 0


0 + 2 is 2
2 + 6 is 8
8 + 3 is 11
11 + 8 is 19
19 is the answer.
Then the second part of each sum is a number from the list, nums . If we
call the number from the list num , the main calculation line in the loop
could be

nextSum = sum + num

The trick is to use the same line of code the next time through the loop.
That means what was nextSum in one pass becomes the sum in the next
pass. One way to handle that is:

sum = 0
for num in nums:
nextSum = sum + num
sum = nextSum

Do you see the pattern? Again it is

initialization
loop heading:
main work to be repeated
preparation for the next time through the loop

Sometimes the two general loop steps can be combined. This is such a
case. Since nextSum is only used once, we can just substitute its value
( sum ) where it is used and simplify to:
«
sum = 0
for num in nums:
sum = sum + num

so the whole function, with the return statement is:

1 def sumList(nums):
2 '''Return the sum of the numbers in the list nums.'''
3 sum = 0
4 for num in nums:
5 sum = sum + num
6 return sum

The example program sumNums.py has the code above with the following
line added at the end to test the function (not indented). Run sumNums.py .

print(sumList([5, 2, 4, 7]))

The pattern used here is certainly successive modification (of the sum
variable). It is useful to give a more specialized name for this version of
the pattern here. It follows an accumulation pattern:

initialize the accumulation to include none of the sequence ( sum = 0 here)


for in :
item sequence
new value of accumulation = result of combining item with last value
of accumulation

This pattern will work in many other situations besides adding numbers.

1.13.7.1. Play Computer sumList Exercise


Suppose the function sumList , defined above, is called with the
parameter [5, 2, 4, 7] . Play computer on this call, using the file
playComputerSumStub.rtf , opened from an operating system window for
the examples directory. Do not open in Idle. The file should come up in
your usual word processor. Immediately save the file as
playComputerSum.rtf , and fill in blank cells in the table.

Make sure there is a row in the table for each line executed in the
program, with a separate line entry for each time a line is executed. In
each row enter which program line is being executed, and show all
changes caused to variables by the execution of that one line. Display
line numbers as shown in the margin beside the example code in the
Tutorial. (The separate Python files themselves do not show the line
numbers.) A table is started for you below. The final row that you enter
in your your table should be for an execution of line numbered 6 in the
code, and your comment can be, “return 18”.

If the same variable value in one column repeats through several rows, it
is more convenient just leave the later entries blank, rather than keep
copying. With this convention, the current value of a variable is the last
value recorded in a previous line in the table.

This is the first “Play Computer” exercise with a loop. Be sure to look
back at the earlier play computer examples. The lines in the loop (and
hence their line numbers) repeat multiple times as rows in the table, as
you follow the loop one time through after another!

The original parameter, which does not change, does not have a column
in the table, for compactness. The start of the table is shown below. As
shown in the first comment, throughout the function call, nums is

[5, 2, 4, 7]

Line sum num comment


1 - - set nums to [5, 2, 4, 7]; skip line 2 doc string
3

1.13.7.2. Test sumList Exercise


Write a program testSumList.py which includes a main function to test the
sumList function several times. Include a test for the extreme case, with
an empty list.

1.13.7.3. Join All Exercise


* Complete the following function. This starting code is in joinAllStub.py .
Save it to the new name joinAll.py . Note the way an example is given
in the documentation string. It simulates the use of the function in the
Shell. This is a common convention:

def joinStrings(stringList):
'''Join all the strings in stringList into one string,
and return the result, NOT printing it. For example:

>>> s = joinStrings(['very', 'hot', 'day']) # returns string


>>> print(s)
veryhotday
'''

First Hint: [5] Second Hint: [6]

1.13.8. More Playing Computer


Testing code by running it is fine, but looking at the results does not
mean you really understand what is going on, particularly if there is an
error! People who do not understand what is happening are likely to
make random changes to their code in an attempt to fix errors. This is a
very bad, increasingly self-defeating practice, since you are likely to
never learn where the real problem lies, and the same problem is likely
to come back to bite you.

It is important to be able to predict accurately what code will do. We have


illustrated playing computer on a variety of small chunks of code.

Playing computer can help you find bugs (errors in your code). Some
errors are syntax errors caught by the interpreter in translation. Some
errors are only caught by the interpreter during execution, like failing to
have a value for a variable you use. Other errors are not caught by the
interpreter at all - you just get the wrong answer. These are called
logical errors. Earlier logical errors can also trigger an execution error
later. This is when playing computer is particularly useful.

A common error in trying to write the numberList function would be to


have the following code (extracted from numberEntriesWRONG.py ):

def numberList(items): #WRONG code for illustration


1
'''Print each item in a list items, numbered in
2
order.'''
3
for item in items:
4
number = 1
5
print(number, item)
6
number = number + 1

You can run this code in and see that it produces the wrong answer. If
you play computer on the call to numberList(['apples', 'pears',
'bananas']) , you can see the problem:

Line item number comment


1 - - set items to [‘apples’, ‘pears’, ‘bananas’]
3 ‘apples’ - start with item as first in sequence
4 ‘apples’ 1
5 ‘apples’ 1 1 print: 1 apples
6 ‘apples’ 2 2 = 1+1
3 ‘pears’ 2 on to the next element in sequence
4 ‘pears’ 1
5 ‘pears’ 1 print: 1 pears OOPS!

If you go step by step you should see where the incorrect 1 came from:
the initialization is repeated each time in the loop at line 4, undoing the
incrementing of number in line 6, messing up your count.

Warning: Always be careful that your one-time initialization for a loop


goes before the loop, not in it!

Functions can also return values. Consider the Python for this
mathematical sequence: define the function m(x) = 5x, let y = 3; find
m(y) + m(2y-1):

1 def m(x): # mathematical function m


2 return 5*x
3
4 y = 3
5 print(m(y) + m(2*y-1))

This code is in example mathfunc.py . A similar example was considered


in Returned Function Values, but now add the idea of playing computer
and recording the sequence in a table. Like when you simplify a
mathematical expression, Python must complete the innermost parts
first. Tracking the changes means following the function calls carefully
and using the values returned. Again a dash ‘-‘ is used in the table to
indicate an undefined variable. Not only are local variables like formal
parameters undefined before they are first used, they are also undefined
after the termination of the function,

Line x y Comment
1-3 Remember definition of m
4 - 3
5 - 3 start on: print(m(y) + m(2*y-1)); first want m(y), which is
m(3)
1 3 3 pass 3 to function m, so x =3
2 3 3 return 5*3 = 15
5 - 3 substitute result: print(15 + m(2*y-1)), want m(2*y-1),
which is m(2*3-1) = m(5)
1 5 3 pass 5 to function m, so x=5
2 5 3 return 5*5 = 25
5 - 3 substitute result: print(15 + 25), so calculate and print 40

Thus far most of the code given has been motivated first, so you are
likely to have an idea what to expect. You may need to read code written
by someone else (or even yourself a while back!) where you are not
sure what is intended. Also you might make a mistake and accidental
write code that does something unintended! If you really understand how
Python works, one line at a time, you should be able to play computer
and follow at least short code sequences that have not been explained
before. It is useful to read another person’s code and try to follow it. The
next exercises also provides code that has not been explained first, or
has a mistake.

1.13.8.1. Play Computer Odd Loop Exercise


* Work in a word processor (not Idle!), starting from example
playComputerStub.rtf , and save the file as playComputer.rtf . The file has
tables set up for this and the following two exercise.

Play computer on the following code:

1 x = 0 # Exercise Play Computer Loop


2 y = 1
3 for n in [5, 4, 6]:
4 x = x + y*n
5 y = y + 1
6 print(x)

Reality check: 31 is printed when line 6 finally executes. The start of the
table for this exercise is shown below.

Line x y n Comment
1 0

1.13.8.2. Play Computer Error Exercise


* In a word processor add to the file playComputer.rtf , started in the
previous exercise.

The following code is supposed to compute the product of the numbers


in a list. For instance product([5, 4, 6]) should calculate and return
5*4*6=120 in steps, calculating 5, 5*4=20 and 20*6=120.

1 def product(nums): #Play Computer Error Exercise


2 for n in nums:
3 prod = 1
4 prod = prod*n
5 return prod

The code for this exercise appears in the example file


numProductWrong.py .

A major use of playing computer is to see exactly where the data that
you expect gets messed up. Play computer on a call to product([5, 4,
6]) until you see that it makes a mistake. Then you can stop and fix it:
First copy numProductWrong.py to numProduct.py , and fix the new file.

The table headings and the first row of the table for this exercise are
shown below.

Line n prod Comment


1 - - Set nums to [5, 4, 6]
2

Save it as numProduct.py and fix the error you found (and save again!).

1.13.8.3. Play Computer Functions Exercise


* In a word processor once again add to the file playComputer.rtf , started
in the previous exercises.

Play computer on the following code:

1 def f(x): #Play Computer Functions Exercise


2 return x+4
3 print(f(3)*f(6))

Reality check: 70 is printed.

The table headings and the first row of the table for this exercise are
shown below.

Line x Comment
1-2 Remember f definition
3

Look at the example above with a table showing a function returning a


value!

1.13.9. The print function keyword end

By default the print function adds a newline to the end of the string being
printed. This can be overridden by including the keyword parameter end .
The keyword end can be set equal to any string. The most common
replacements are the empty string or a single blank. If you also use the
keyword parameter sep , these keyword parameters may be in either
order, but they must come at the end of the parameter list. Read the
illustrations:

print('all', 'on', 'same', 'line')


print('different line')

is equivalent to

print('all', 'on' , end=' ')


print('same', end=' ')
print('line')
print('different line')

This does not work directly in the shell (where you are always forced to a
new line at the end). It does work in a program, but it is not very useful
except in a loop!

Suppose I want to print a line with all the elements of a list, separated by
spaces, but not on separate lines. I can use the end keyword set to a
space in the loop. Can you figure out in your head what this example file
endSpace1.py does? Then try it:

def listOnOneLine(items):
for item in items:
print(item, end=' ')

listOnOneLine(['apple', 'banana', 'pear'])


print('This may not be what you expected!')

If you still want to go on to a new line at the end of the loop, you must
include a print function that does advance to the next line, once, after
the loop. Try this variation, endSpace2.py

def listOnOneLine(items):
for item in items:
print(item, end=' ')
print()

listOnOneLine(['apple', 'banana', 'pear'])


print('This is probably better!')

[5] This is a form of accumulation, but not quite the same as adding
numbers.
[6] “Start with nothing accumulated” does not mean 0, here. Think
what is appropriate.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Jun 05, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Table Of Contents 1.14. Decimals, Floats, and Floating


1.14. Decimals, Floats, and
Floating Point Arithmetic Point Arithmetic
1.14.1. Floats, Division,
Mixed Types Floating point numbers like 12.345 are a basic type, but there are some
1.14.2. Exponentiation, complications due to their inexactness. This section may be deferred
Square Roots
1.14.3. String Formats until you actually need numbers other than integers.
for Float Precision
1.14.3.1. Floating
Point Exercise
1.14.1. Floats, Division, Mixed Types
Previous topic
1.13. Loops and Sequences As you moved on in school from your first integer division to fractions and
decimals, you probably thought of 6/8 as a fraction and could convert to
Next topic a decimal .75. Python can do decimal calculations, too, approximately.
1.15. Summary
Try all set-off lines in this section in the Shell:
This Page
Show Source 6/8
6/3
Quick search 2.3/25.7

There is more going on here than meets the eye. As you should know,
Go decimal representations of values can be a pain. They may not be able
to be expressed with a finite number of characters. Try
Enter search terms or a module,
class or function name.
2/3

Also, as you may have had emphasized in science class, real number
measurements are often not exact, and so the results of calculations
with them are not exact. In fact there are an infinite number of real
number just between 0 and 1, and a computer is finite. It cannot store all
those numbers exactly! On the other hand, Python does store integers
exactly (well at least far past the number of atoms in the universe -
eventually even integers could get too big to store in a computer). The
difference in the way integers and decimals are stored and processed
leads to decimals and integers being different types in Python. Try

type(3.5)

Note that 3.5 is of type ‘float’, not ‘decimal’. There are several reasons for
that name having to do with the actual way the type is stored internally.
“Decimal” implies base ten, our normal way for writing numbers with ten
digits 0,1,2,3,4,5,6,7,8,9. Computers actually use base two, with only
two symbols 0,1. (Did you note what symbols were in the machine
language in Context?) Also floats use an encoding something like
scientific notation from science class, with exponents that allow the
decimal point to move or “float”, as in the decimal case: 2345.6 =
(2.3456)103

Try

type(-2)
type(-2.0)

Even a number that is actually an integer can be represented in the float


type if a decimal point is included.

Always be sure to remember that floats may not be exact. The use of
base two makes this true even in cases where decimal numbers can be
expressed exactly! More on that in String Formats for Float Precision.

It is sometimes important to know the numeric type of the result of a


binary operation. Any combination of +, -, and * with operands of type int
produces an int. If there is an operation /, or if either operand is of type
float, the result is float. Try each in the Shell (and guess the resulting
type): [1]

3.3 - 1.1
2.0 + 3
2*2.5

[1] Python 3.1 does what you would expect mathematically with an
expression like (1/2)*6.5

Caution: This is not the case in other common languages like Java
and C++ (or with versions of Python before 3.0). They treat the /
operation with integers like the current Python //, so the result of
the expression above is 0, since 1//2 is 0.

1.14.2. Exponentiation, Square Roots


Exponentiation is finding powers. In mathematical notation, (3)(3)(3)
(3)=34. In Python there is no fancy typography with raised exponent
symbols like the 4, so Python uses ** before a power: Try in the Shell:

3**4
5*2**3

If you expected 1000 for the second expression, remember


exponentiation has even higher precedence than multiplication and
division: 2**3 is 2*2*2 or 8, and 5*8 is 40.

Exponents do not need to be integers. A useful example is the 0.5


power: it produces a square root. Try in the Shell:
9**.5
2**.5

The result of a power operation is of int type only if both parameters are
integers and the correct result is an integer.

1.14.3. String Formats for Float Precision


You generally do not want to display a floating point result of a
calculation in its raw form, often with an enormous number of digits after
the decimal point, like 23.457413902458498. You are likely to prefer
rounding it to something like 23.46. There are two approaches.

First there is a format function (not method) with a second parameter


allowed to specialize the formatting of objects as strings. Read the
following example interpreter sequence showing possibilities when a
float is being formatted:

>>> x = 23.457413902458498
>>> s = format(x, '.5f')
>>> s
'23.45741'
>>> format(x, '.2f')
'23.46'
>>> x
23.457413902458498

Note that the results are rounded not truncated: the result to two places
is 23.46, not 23.45. The formatting string '.5f' means round to 5 places
after the decimal point. Similarly '.2f' means round to two decimal
places.

Warning: This format function returns the formatted string. It does


not change the parameters. As a complete statement in a program
format(x, '.2f') , is useless: The '23.46' gets returned and thrown
away, with no effect on x .

The first version, saving the formatted value to s , will allow the formatted
string to be used again (as s ).

This rounding notation can also be placed after a colon inside the braces
of format strings, for use with the string format method. Recall there are
many ways to indicate what values to substitute into a format string. One
way is just to omit any reference to the variables and substitute
parameters in order. Separate from the value to substitute, and following
any notation for it, you can put a colon : and the formatting information
we used in the simple format method above (like .5f . but with NO
« quotes):
>>> x = 2.876543
>>> 'longer: {:.5f}, shorter: {:.3f}.'.format(x, x)
'longer: 2.87654, shorter: 2.877.'

The instructions for the data to insert can also be given by position index:

>>> x = 2.876543
>>> 'longer: {0:.5f}, shorter: {0:.3f}.'.format(x)
'longer: 2.87654, shorter: 2.877.'

or using dictionary keys:

>>> x = 2.876543
>>> 'longer: {x:.5f}, shorter: {x:.3f}.'.format(**locals())
'longer: 2.87654, shorter: 2.877.'

In each of these approaches, the colon and formatting specification come


at the end of the expression inside the braces, just before the closing } .
This follows the { and symbols (if any) identifying what value to use for
the substitution.

There are many more fancy formatting options for the string format
method that we will not discuss.

Going to the opposite extreme, and using formatting with many digits,
you can check that Python does not necessarily remember simple
decimal numbers exactly:

>>> format(.1, '.20f')


0.10000000000000000555
>>> format(.2, '.20f')
'0.20000000000000001110'
>>> format(.1 + .2, '.20f')
'0.30000000000000004441'
>>> format(.3, '.20f')
'0.29999999999999998890'

Python stores the numbers correctly to about 16 or 17 digits. You may


not care about such slight errors, but you will be able to check in
Chapter 3 that if Python tests the expressions .1 + .2 and .3 for equality,
it decides that they are not equal! In fact, as you can see above, the
approximations that Python stores for the two expressions are not
exactly equal.

Warning: Do not depend on the exactness of floating point


arithmetic, even for apparently simple expressions!

The floating point formatting code in this section is also in example


program floatFormat.py .
1.14.3.1. Floating Point Exercise
Write a program, discount.py , that prompts the user for an original price
and for a discount percentage and prints out the new price to the
nearest cent. For example if the user enters 2.89 for the price and 20 for
the discount percentage, the value would be

(1 - 20/100) * 2.89

rounded to two decimal places, 2.31. For price .65 with a 25 percent
discount, the value would be

(1 - 25/100) * .65

rounded to two decimal places, .49. [2]

Write the general calculation code following the pattern of the


calculations illustrated in the two concrete examples.

[2] In Python 3.0+, the previous expressions make sense, but in


earlier versions of Python and in other languages like C++ and
Java, where there are not separate division operators // and /,
these expressions would be wrong because of the multiple
meanings of the operator / with different types. The expressions
would work in these other languages if, for example, 100 were
replaced by 100.0.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on May 28, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

Previous topic 1.15. Summary


1.14. Decimals, Floats, and
Floating Point Arithmetic Section references in square brackets indicate where an idea was first
discussed.
Next topic
2. Objects and Methods Where Python syntax is illustrated, the typeface continues to indicate the
This Page category of each part:

Show Source Typeface Meaning


Typewriter font
Quick search Text to be written verbatim
Emphasized A place where you can use an arbitrary
expression.
Go Bold A place where you can use an arbitrary identifier.
Normal text A description of what goes in that position,
Enter search terms or a module,
class or function name. without giving explicit syntax

If there are several variations on a particular part of the syntax,


alternatives will be show on successive lines.

To emphasize the successive parts of the syntax, space will generally be


left around symbol and punctuation characters, but the space is not
required in actual use.

A. Python Shell

1. A Shell window may be opened in Idle Run Python Shell


[Windows in Idle]
2. Entering commands:
a. Commands may be entered at the >>> prompt. [Addition
and Subtraction]
b. If the Shell detects that a command is not finished at the
end of the line, a continuation line is shown with no >>> .
[Multiplication, Parentheses, and Precedence]
c. Statements with a heading ending in a colon followed by
an indented block, must be terminated with an empty
line. [Basic for Loops]
d. The Shell evaluates a completed command
immediately, displaying any result other than None ,
starting on the next line. [Addition and Subtraction]
e. The Shell remembers variable and function names.
[Variables and Assignment]
3. An earlier Shell line may to copied and edited by clicking
anywhere in the previously displayed line and then pressing
Enter .
B. Idle editing
1. Start a new window from the File menu by selecting New,
Open..., or Recent Files. [Loading a Program in the Idle
Editor, and Running It]
2. Make your Python file names explicitly end with ‘.py’ [Literals
and Identifiers]
C. To run a program from an Idle Editor Window:

1. Select Run -> Run Module or press function key F5 . The


program runs in the Shell window, after resetting the shell so
all old names are forgotten. [Loading a Program in the Idle
Editor, and Running It]
a. If the program is expecting keyboard input, the text
cursor should appear at the end of the Shell history. If
you somehow move the cursor elsewhere, you must
explicitly move it back. [The Idle Editor and Execution]
b. BUG WORKAROUND: If you were running a program
that was expecting keyboard input when you
terminated it to start the latest run, you will need to start
by pressing the Enter key once or maybe twice to clear
the old pending wait for input. [A Pre Python 3.3 Idle
Bug]
c. Press Ctrl-C to stop a running program in a long or
infinite loop.
d. After a program terminates, the Shell remembers
function definitions and variable names define outside
of any function. [A First Function Definition]
D. Errors come in three categories:

1. Syntax errors: text that the interpreter recognizes as illegal


when first reading it. This prevents execution of your code.
Python lets you know where it realized there was an error.
Sometimes this is the exact location, but the actual error
could be anywhere earlier, often on the previous line.
[Variables and Assignment]
2. Execution errors: The first illegal action is detected while
running your command or program. The source of the error
could be in the line where execution fails, or it could be an
earlier logical error that only later forces an execution error.
[Variables and Assignment]
Execution errors generate a traceback. [Function
Parameters]
3. Logical errors: When Python detects nothing illegal, but you
do not get the results you desire. These errors are the
hardest to trace down. Playing computer and additional print
functions help. [More Playing Computer]
E. Type int , (short for integer):

1. Literal integer values may not contain a decimal point. [Floats,


Division, Mixed Types]
2. Integers may be arbitrarily large and are stored exactly.
[Floats, Division, Mixed Types]
3. Integers have normal operations, with usual precedence
(highest listed first):
a. ** : exponentiation (5**3 means 5*5*5) [Exponentiation,
Square Roots]
b. * , / , // , % : multiplication, division with float result,
integer division (ignoring any remainder), just the
remainder from division [Division and Remainders]
c. + , - : addition, subtraction [Addition and Subtraction]
F. Type float , (short for floating point): approximations of real
numbers

1. Literal values must contain a decimal point to distinguish


them from the int type [Floats, Division, Mixed Types]
2. Approximates a wide range of values [Floats, Division, Mixed
Types]
3. Does not dependably store numbers exactly - even numbers
with simple decimal representation [Floats, Division, Mixed
Types]
4. Has the same operation symbols as for integers, but always
with a float result [Floats, Division, Mixed Types]
5. A mixed binary operation with an integer and a float
produces a float result. [Floats, Division, Mixed Types]
G. Type str , (short for string): Literal values contain a sequence of
characters enclosed in matching quotes.

1. Enclosed in ' or " : The string must be on one line. [String


Delimiters, Part I]

2. Enclosed in ''' or """ : The string may include multiple lines


in the source file. [Triple Quoted String Literals]

3. Escape codes inside literals include \' for a single quote and
\n for a newline. [Escape Codes]

4. Binary operations (operation symbols have the same


precedence order as when the symbols are used in
arithmetic) [String Concatenation]

a. stringExpression1 + stringExpression2

concatenation (running together) of the two strings

b. stringExpression * integerExpression
integerExpression * stringExpression

Repeat the string the number of times given by the


integer expression.
5. string format method:

a. stringFormatExpression .format( parameter0 ,


parameter1 , parameter2 , ... )

[String Format Operation] where


stringFormatExpression is any string with an arbitrary
number of places for format substitutions in it.
Formatted substitutions are enclosed in braces. A digit
inside the braces will indicate which parameter value is
substituted, counting from 0. If digits are left out, the
format parameters are substituted in order. The
expression inside the braces can end with a colon :
followed by a format specifying string such as .#f
where # can be a non negative integer: substitute a
numerical value rounded to the specified number of
places beyond the decimal point. [Floats, Division,
Mixed Types]

Example:

'word: {}, number: {}, formatted number:


{:.3f}.'.format('Joe', 23, 2.1357)

evaluates to:

'word: Joe, number: 23, formatted number: 2.136.'

b. stringFormatExpression .format( ** dictionary )

The format expressions are the same as above except


that a key name from a dictionary appears inside the
braces. The dictionary referenced appears in the
parameter list preceded by ** . Any value to be
substituted is then taken from the dictionary by
accessing the key. Example: If defs is a dictionary with
defs['name'] equaling 'Joe' , defs['num'] equaling 23 ,
defs['dec'] equaling 2.13579 , then

'word: {name}, number: {num}, formatted number:


{dec:.3f}.'.format(**defs}

evaluates to the same string as in the previous


example. [Dictionaries and String Formatting]

In particular, the dictionary reference can the the


dictionary of all local variable names, by making the
parameter to format be **locals() . [Dictionaries and
Python Variables]

6. Strings are a kind of sequence.

H. Type list

[ expression1 , expression2 , and so on ]


[ expression ]
[]

1. A literal list consists of a comma separated collection of


values all enclosed in square brackets. There may be many,
one, or no elements in the list. [The list Type]
2. A list is a kind of sequence, so it may be used as the
sequence in a for statement heading. [Basic for Loops]
I. Type dict (short for dictionary)

dict()

returns an empty dictionary

1. A dictionary provides an association of each key to its value.


The key can be any immutable type, with includes numbers
and strings. [Definition and Use of Dictionaries]

2. dictName [ keyExpression ] = valueExpression

associates in the dictionary dictName the key derived from


evaluating keyExpression with the value derived from
evaluating valueExpression. [Definition and Use of
Dictionaries]

3. Used in an expression,

dictName [ keyExpression ]

evaluates to the value in the dictionary dictName coming from


the key obtained by evaluating keyExpression. [Definition
and Use of Dictionaries]

J. Type of None : This literal value has its own special type. None
indicates the absence of a regular object.

K. Identifiers

1. Identifiers are names for Python objects [Literals and


Identifiers]
2. They may only contain letters, digits, and the underscore, and
cannot start with a digit. They are case sensitive. [Literals
and Identifiers]
3. You cannot use a reserved word as an identifier, nor are you
recommended to redefine an identifier predefined by Python.
In the Idle editor you are safe if your identifier names remain
colored black. [Literals and Identifiers]
4. By convention, multi-word identifiers either [Literals and
Identifiers]
a. use underscores in place of blanks (since blanks are
illegal is identifiers), as in initial_account_balance
b. use camel-case: all lowercase except for the starting
letter of the second and later words, as in
initialAccountBalance
L. Variables are identifiers used to name Python data [Variables and
Assignment]

1. When a variable is used in an expression, its latest value is


substituted. [Variables and Assignment]
M. Statements

1. Assignment statement: [Variables and Assignment]

variable = expression

a. The expression on the right is evaluated, using the


latest values of all variables, and calculating all
operations or functions specified.
b. The expression value is associated with the variable
named on the left, removing any earlier association
with the name.
2. For-statement

for item in sequence :


consistently indented statement block, which may use the
variable item

For each element in the sequence, repeat the statement


block substituting the next element in the sequence for the
name variable name item. See Programming Patterns for
patterns of use. [Basic for Loops]

3. Return statement

return expression

This is used only in a function definition, causing the function


to immediately terminate and return the value of expression
to the calling code, effectively acting as if the function call
was replaced by this returned value. [Returned Function
Values]

N. Function calls
functionName ( expression , expression , and so on )

1. The number of expressions must correspond to a number of


parameters allowed by the function’s definition. [Function
Parameters]
2. Even if there are no parameters, the parentheses must be
included to distinguish the name of the function from a
request to call the function. [A First Function Definition]
3. Each expression is called an actual parameter. Each actual
parameter is evaluated and the values are passed to the
code for the function, which executes its defined steps and
may return a value. If the function call was a part of a larger
expression, the returned value is used to evaluate the larger
expression in the place where the function call was.
[Function Parameters]
4. If nothing is returned explicitly, the function returns None .
5. Function calls may also be used as statements, in which case
any value that is returned is ignored (except if entered
directly into the shell, which prints any returned value other
than None ).
6. Keyword arguments are a special case. They have been used
optionally at the end of the parameter list for print.
O. Functions that are built-in

1. Print function: [Print Function, Part I] [The print function


keyword end]

print( expression )
print( expression , expression , expression )
print( expression , expression , expression, sep= strVal,
end= strVal )
print()

a. Print the value of each expression in the list to the


standard place for output (usually the screen)
separating each value by individual blanks unless the
keyword argument sep is specified to change it. There
can be any number of expressions (not just 1 or 3 as
illustrated)
b. The string printed ends with a newline unless the
keyword argument end is specified to change it.
c. With no expression, the statement only advances to a
new line.
2. A type name can be used as function to do obvious
conversions to the type, as in int('234') , float(123) ,
str(123) . [Numbers and Strings of Digits]

3. type( expression )
Return the type of the value of the expression. [String
Delimiters, Part I]

4. input( promptString )

Print the promptString to the screen; wait for the user to enter
a line from the keyboard, ending with Enter . Return the
character sequence as a string [The input Function]

5. len( sequence )

Return the number of elements in the sequence [Whirlwind


Introduction To Types and Functions]

6. range( expression )

Require expression to have a non negative integer value, call


it n. Generate a sequence with length n, consisting of the
numbers 0 through n-1. For example range(4) generates the
sequence 0, 1, 2, 3 [The range Function, Part 1]

7. max( expression , expression , and so on )

Return the maximum of all the expressions listed. [Whirlwind


Introduction To Types and Functions]

8. format( expression , formatString )

If expression is numeric, the format string can be in the form


'.#f' , where the # gets replaced by a nonnegative integer,
and the result is a string with the value of the expression
rounded to the specified number of digits beyond the decimal
point. [Floats, Division, Mixed Types]

P. Functions defined by a user:

def functionName ( parameter1 , parameter2 , and so on ) :


consistently indented statement block, which may include a
return statement
«
1. There may be any number of parameters. The parentheses
must be included even if there are no parameters. [Function
Parameters]
2. When a function is first defined, it is only remembered: its
lines are not executed. [A First Function Definition]
3. When the function is later called in other code, the actual
parameters in the function call are used to initialize the local
variables parameter1, parameter2, and so on in the same
order as the actual parameters. [Function Parameters]
4. The local variables of a function are independent of the local
names of any function defined outside of this function. The
local variables must be initialized before use, and the names
lose any association with their values when the function
execution terminates. [Local Scope]
5. If a return statement is reached, any further statements in the
function are ignored. [Returned Function Values]
6. Functions should be used to :
a. Emphasize that the code corresponds to one idea and
give an easily recognizable name. [A First Function
Definition]
b. Avoid repetition. If a basic idea is repeated with just the
data changing, it will be easier to follow and use if it is
coded once as a function with parameters, that gets
called with the appropriate actual parameters when
needed. [Function Parameters]
c. It is good to separate the internal processing of data
from the input and output of data. This typically means
placing the processing of data and the return of the
result in a function. [Function Parameters]
d. Separate responsibilities: The consumer of a function
only needs to know the name, parameter usage, and
meaning of any returned value. Only the writer of a
function needs to know the implementation of a
function. [Two Roles: Writer and Consumer of
Functions]
Q. Modules (program files)

1. A module may start with a documentation string. [Program


Documentation String]
2. Define your functions in your module. If the module is
intended as a main program called only one way, a
convention is make your execution just be calling a function
called main. [Multiple Function Definitions]
3. Avoid defining variable outside of your functions. Names for
constant (unchanging) values are a reasonable exception.
[Global Constants]
R. Documentation String: A string, often a multi-line (triple quoted)
string that may appear in two places:

1. At the very beginning of a file: This should give overall


introductory information about the file [Program
Documentation String]
2. As the very first entry in the body of a function: This should
describe: [Definition and Use of Dictionaries]
a. The return value of the function (if there is one)
b. Anything about the parameters that is not totally obvious
from the names
c. Anything about the results from the function that is not
obvious from the name
S. Programming Patterns

1. Input-calculate-Output: This is the simplest overall program


model. First obtain all the data you need (for instance by
prompting the user for keyboard input). Calculate what you
need from this data. Output the data (for instance to the
screen with print functions). [The input Function]

2. Repetitive patterns: These patterns are all associated with


loops. Loops are essential if the number of repetitions
depends on dynamic data in the program. Even if you could
avoid a loop by repeating code, a loop is usually a better
choice to make the repetitive logic of your program clear to
all.

a. Exact repetition some number of times: If the number of


time to repeat is n:

for i in range( n ):
actions to be repeated

Here the variable i is included only because there must


be a variable name in a for loop. [Simple Repeat Loop]

b. For-each loop: Do the same sort of thing for each item


in a specified sequence. [Basic for Loops]

for item in sequence :


actions to be done with each item

c. Successive modification loop: Repeat a basic idea, but


where the data involved each time changes via a
pattern that is coded in the loop to convert the previous
data into the data needed the next time through the
loop [Successive Modification Loops]:

initialize all variables that will be successively modified


in the loop
loop heading for the repetition :
actions to be in each loop with the current variable
values
modify the variable values to prepare for the next
time through the loop

d. Accumulation loop: A sequence of items need to be


combined. This works where the accumulation of all the
items can be approached incrementally, combining one
after another with the accumulation so far
[Accumulation Loops]:
initialize the accumulation to include none of the
sequence
for item in sequence :
new value of accumulation = partial result

where the partial result comes from combining item with


the current value of accumulation

T. Playing computer: following code line by line of execution. This


either tests that you understand your code (and it works right) or it
helps you find where it goes wrong. [Updating Variables,
Successive Modification Loops, More Playing Computer]

1. Make sure line numbers are labeled


2. Make a table with heading for line numbers, all variables that
will be changing, and comments
3. Follow the order of execution, one statement at a time, being
careful to update variable values and only use the latest
variable values, and carefully following the flow of control
through loops and into and out of function calls.
Hands-on Python Tutorial » 1. Beginning With Python » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » previous | next | index

Previous topic 2. Objects and Methods


1.15. Summary
2.1. Strings, Part III
Next topic 2.1.1. Object Orientation
2.1. Strings, Part III 2.1.2. String Indices
This Page 2.1.3. String Slices
2.1.4. Index Variables
Show Source
2.1.5. split
Quick search 2.1.6. join
2.1.6.1. Underscore Exercise
2.1.6.2. Acronym Exercise
Go
2.1.7. Further Exploration
2.2. More Classes and Methods
Enter search terms or a module,
class or function name. 2.2.1. Appending to a List
2.2.2. Sets
2.2.3. Constructors
2.3. Mad Libs Revisited
2.3.1. A Function to Ease the Creation of Mad Libs
2.3.2. Creative Problem Solving Steps
2.3.3. The Revised Mad Lib Program
2.3.3.1. Substring Locations Exercise
2.4. Graphics
2.4.1. A Graphics Introduction
2.4.2. Sample Graphics Programs
2.4.3. A Windows Operating System Specialization: .pyw
2.4.4. Graphics.py vs. Event Driven Graphics
2.4.5. The Documentation for graphics.py
2.4.5.1. Scene Exercise
2.4.5.2. Changing Scene Exercise
2.4.6. Issues with Mutable Objects
« 2.4.7. More on Mutable and Immutable Types
2.4.8. Animation
2.4.8.1. Nose in Face Exercise
2.4.8.2. Faces Exercise
2.4.8.3. Moving Faces Exercise
2.4.9. Entry Objects
2.4.10. Color Names
2.4.11. Custom Colors
2.4.12. Random Colors
2.4.12.1. Ranges Exercise
2.4.12.2. Text Triangle Exercise
2.5. Files
2.5.1. Writing Files
2.5.2. Reading Files
2.5.2.1. PrintUpper Exercise
2.5.2.2. Mad Lib File Exercise
2.6. Summary

Hands-on Python Tutorial » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Sep 08, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 2. Objects and Methods » previous | next | index

Table Of Contents 2.1. Strings, Part III


2.1. Strings, Part III
2.1.1. Object Orientation
2.1.2. String Indices
2.1.3. String Slices 2.1.1. Object Orientation
2.1.4. Index Variables
2.1.5. split Python is an object-oriented language. Every piece of data and even
2.1.6. join functions and types are objects. The term object-oriented is used to
2.1.6.1. distinguish Python from earlier languages, classified as procedural
Underscore
languages, where types of data and the operations on them were not
Exercise
2.1.6.2. Acronym connected in the language. The functions we have used so far follow the
Exercise older procedural programming syntax. In the newer paradigm of object-
2.1.7. Further oriented programming, all data are in objects, and a core group of
Exploration operations that can be done on some particular type of object are tightly
bound to the object and called the object’s methods.
Previous topic
2. Objects and Methods For example, strings are objects, and strings “know how” to produce an
Next topic uppercase version of themselves. Try in the Shell:
2.2. More Classes and
Methods s = 'Hello!'
s.upper()
This Page
Here upper is a method associated with strings. This means upper is a
Show Source
function that is bound to the string before the dot. This function is bound
Quick search both logically, and as we see in the new notation, also syntactically. One
way to think about it is that each type of data knows operations
(methods) that can be applied to it. The expression s.upper() calls the
Go
method upper that is bound to the string s and returns a new uppercase
Enter search terms or a module, string result based on s .
class or function name.
Strings are immutable, so no string method can change the original
string, it can only return a new string. Confirm this by entering each line
individually in the Shell to see the original s is unchanged:

s
s2 = s.upper()
s2
s

We are using the new object syntax:

object . method ( )

meaning that the method associated with the object’s type is applied to
the object. This is just a special syntax for a function call with an object.

Another string method is lower , analogous to upper, but producing a


lowercase result.
Test yourself: How would you write the expression to produce a
lowercase version of the string s ? Answer: [1]

Try it in the Shell.

Test yourself in the Shell: How would you use this string s and both the
lower and upper methods to create the string 'hello!HELLO!' ? Hint: [2]
Answer: [3]

Many methods also take additional parameters between the


parentheses, using the more general syntax:

object . method ( parameters )

The first of many such methods we will introduce is count :

Syntax for count :

s.count(sub)

Count and return the number of repetitions of a string sub that appear as
substrings inside the string s .

Read and make sure you see the answers are correct:

>>> tale = 'This is the best of times.'


>>> tale.count('i')
3
>>> tale.count('is')
2
>>> tale.count('That')
0
>>> tale.count(' ')
5

There is a blank between the quotes in the line above. Blanks are
characters like any other (except you can’t see them)!

Just as the parameter can be replaced by a literal or any expression, the


object to which a method is bound with the dot may also be given by a
literal, or a variable name, or any expression that evaluates to the right
kind of object in its place. This is true for any method call.

Technically the dot between the object and the method name is an
operator, and operators have different levels of precedence. It is
important to realize that this dot operator has the highest possible
precedence. Read and see the difference parentheses make in the
expressions:

>>> 'hello ' + 'there'.upper()


'hello THERE'
>>> ('hello ' + 'there').upper()
'HELLO THERE'
To see if you understand this precedence, predict the results of each line
and then test in the Shell:

3 * 'X'.count('XXX')
(3 * 'X').count('XXX')

There are 0 ‘XXX’s in ‘X’, but 1 ‘XXX’ in ‘XXX’.

Python lets you see all the methods that are bound to an object (and any
object of its type) with the built-in function dir . To see all string methods,
supply the dir function with any string. For example, try in the Shell:

dir('')

Many of the names in the list start and end with two underscores, like
__add__. These are all associated with methods and pieces of data
used internally by the Python interpreter. You can ignore them for now.
The remaining entries in the list are all user-level methods for strings.
You should see lower and upper among them. Some of the methods are
much more commonly used than others.

Object notation

object . method ( parameters )

has been illustrated so far with just the object type str , but it applies to all
types. Later in the tutorial methods such as the following will be
discussed:

If seq is a list , seq.append(element) appends element to the end of the


list.

If myData is a file , myData.read() will read and return the entire contents
of the file....

2.1.2. String Indices


A string is a sequence of smaller components (individual characters),
and it is often useful to deal with parts of strings. Python indexes the
characters in a string, starting from 0, so for instance, the characters in
the string 'computer' have indices:

character c o m p u t e r
index 0 1 2 3 4 5 6 7
index 0 1 2 3 4 5 6 7

Each index is associated with a character, and you reference the


individual characters much like in a dictionary. Try the following. (You
can skip the comments that make the indices explicit.) Enter in the Shell:

# 01234567
s = 'computer'
s[0]
s[5]
s[8]

You cannot refer directly to a character that is not there. Indices only go
to 7 in the example above.

Recall the len function, which gives the length of a sequence. It works on
strings. Guess the following value, and test in the Shell:

len(s)

A common error is to think the last index will be the same as the length of
the string, but as you saw above, that leads to an execution error. If the
length of some string is 5, what is the index of its last character? What if
the length is 35?

Hopefully you did not count by ones all the way from 0. The indices for a
string of length n are the elements of the sequence range(n) , which goes
from 0 through n-1, or the length of the string minus one, which is 5-1=4
or 35-1 = 34 in these examples.

Sometimes you are interested in the last few elements of a string and do
not want to do calculations like this. Python makes it easy. You can
index from the right end of the string. Since positive integers are used to
index from the front, negative integers are used to index from the right
end, so the more complete table of indices for 'computer' gives two
alternatives for each character:

character c o m p u t e r
index 0 1 2 3 4 5 6 7
index from the right end -8 -7 -6 -5 -4 -3 -2 -1

Predict and test each individual line, continuing in the Shell:

s[-1]
s[-3]
s[-10]

it = 'horse'
len(it)
it[-1]
it[1]

Be careful - remember what the initial index is!


2.1.3. String Slices
It is also useful to extract larger pieces of a string than a single character.
That brings us to slices. Try this expression using slice notation,
continuing in the Shell:

s[0:4]

Note that s[4] is the first character past the slice. The simplest syntax for
a slice of a string s is:

s[ startIndex : pastIndex ]

This refers to the substring of s starting at index startIndex and stopping


just before index pastIndex.

Warning: It confuses many people that the index after the colon is
not the index of the final character in the slice. The second index is
past the end of the slice.

Predict and try each line individually in the Shell:

s[2:5]
s[1:3]

If you omit the first index, the slice starts from the beginning. If you omit
the second index, the slice goes all the way to the end. Predict and try
each line individually in the Shell:

s[:3]
s[5:]
s[:]

Predict and try each line individually in the Shell:

word = 'program'
word[2:4]
word[1:-3]
word[3:]
word[3:3]
word[:1] + word[4:]

Python evaluates slices in a more forgiving manner than when indexing


single characters. In a slice, if you give an index past a limit of where it
could be, Python assumes you mean the actual end. Predict and try
each line individually in the Shell:

word[:9]
word[8:10]
Enter a slice expression using the variable word from above that
produces 'gra' .

A useful string method that uses the ideas of indices and slices is find .

Syntax options for find :

s .find( sub)
s .find( sub, start)
s .find( sub, start, end)

Return the integer index in the string s of the beginning of first complete
occurrence of the substring sub. If sub does not appear inside s, return
-1. The value -1 would be an impossible result if sub were found, so if -1
is returned, sub must not have been found. If parameters start and end
are not included in the parameter list, the search is through the whole
string s . If an integer value is given for start, the search starts at index
start. If an integer value is given for end, the search ends before index
end. In other words if start and end appear, then the search is through
the slice s[start : end], but the index returned is still counted from the
beginning of s .

For example, check that the following make sense. The comment line is
just there to help you count:

>>> # 01234567890
>>> s = 'Mississippi'
>>> s.find('i')
1
>>> s.find('si')
3
>>> s.find('sa')
-1
>>> s.find('si', 4)
6

Predict and try each line in the Shell:

# 0123456789012
line = 'Hello, there!'
line.find('e')
line.find('he')
line.find('e', 10)
line.find('he', 10)

« We will consider more string methods later, but we can already do useful
things with the ones introduced.

Inside the Shell, you can look up documentation on any of the methods
listed with the dir function. Here is a place that you want to refer to the
method itself, not invoke the method, so note that you get help for s.find
not for s.find() . Assuming you defined the string s in the Shell earlier,
try in the Shell

help(s.find)

The Python documentation uses square brackets to indicate optional


elements which get a default value if you leave them out. This shortens
the syntax descriptions.

If you want method documentation when you do not have a variable of


the type created, you can also use the type name. Try in the Shell:

dir(str)
help(str.capitalize)

Indexing and slicing works on any kind of Python sequence, so you can
index or slice lists also.* Read* this Shell session:

>>> vals = [5, 7, 9, 22, 6, 8]


>>> vals[1]
7
>>> vals[-2]
6
>>> vals[1:4]
[7, 9, 22]

Unlike strings, lists are mutable, as you will see in Appending to a List.
Indices and slices can also be used in assignment statements to change
lists, but in this tutorial we not need list indexing, and we will not discuss
this subject further.

2.1.4. Index Variables


All the concrete examples in the last two sections used literal numbers
for the indices. That is fine for learning the idea, but in practice, variables
or expressions are almost always used for indices. As usual the variable
or expression is evaluated before being used. Try in Idle and see that
the example program index1.py makes sense:

s = 'word'
print('The full string is: ', s)
n = len(s)
for i in range(n):
print()
print('i =', i)
print('The letter at index i:', s[i])
print('The part before index i (if any):', s[:i])
print('The part before index i+2:', s[:i+2])

We will use index variables in more practical situations as we explain


more operations with strings.
2.1.5. split

Syntax options for the split method with a string s:

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.

For example, read and follow:

>>> tale = 'This is the best of times.'


>>> tale.split()
['This', 'is', 'the', 'best', 'of', 'times.']
>>> s = 'Mississippi'
>>> s.split('i')
['M', 'ss', 'ss', 'pp', '']
>>> s.split() # no white space
['Mississippi']

Predict and test each line in the Shell:

line = 'Go: Tear some strings apart!'


seq = line.split()
seq
line.split(':')
line.split('ar')
lines = 'This includes\\nsome new\\nlines.'
lines.split()

2.1.6. join

Join is roughly the reverse of split. It joins together a sequence of strings.


The syntax is rather different. The separator sep comes first, since it has
the right type (a string).

Syntax for the join method:

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.

For example (continuing in the Shell from the previous section, using
seq ), follow:

>>> ' '.join(seq)


'Go: Tear some strings apart!'
>>> ''.join(seq)
'Go:Tearsomestringsapart!'
>>> '//'.join(seq)
'Go://Tear//some//strings//apart!'

Predict and try each line, continuing in the Shell:

'##'.join(seq)
':'.join(['one', 'two', 'three'])

The methods split and join are often used in sequence:

2.1.6.1. Underscore Exercise


Write a program underscores.py that would input a phrase from the user
and print out the phrase with the white space between words replaced
by an underscore. For instance if the input is the best one , then it would
print the_best_one . The conversion can be done in one or two statements
using the recent string methods.

2.1.6.2. Acronym Exercise


* An acronym is a string of capital letters formed by taking the first letters
from a phrase. For example, SADD is an acronym for ‘students against
drunk driving’. Note that the acronym should be composed of all capital
letters even if the original words are not. Write a program acronym.py that
has the user input a phrase and then prints the corresponding acronym.

To get you started, here are some things you will need to do. First check
that you understand the basic syntax to accomplish the different
individual tasks: Indicate the proper syntax using a Python function or
operation will allow you to accomplish each task. Invent appropriate
variable names for the different parts. This is not complete instructions!
the idea is to make sure you know the basic syntax to use in all these
situations. See the questions after the list to help you put together the
final program.

1. What type of data will the input be? What type of data will the
output be?
2. Get the phrase from the user.
3. Convert to upper case.
4. Divide the phrase into words.
5. Initialize a new empty list, letters .
6. Get the first letter of each word.
7. Append the first letter to the list letters .
8. Join the letters together, with no space between them.
9. Print the acronym.

Which of these steps is in a loop? What for statement controls this loop?
Put these ideas together and write and test your program acronym.py .
Make sure you use names for the objects that are consistent from one
line to the next! (You might not have done that when you first considered
the syntax and ideas needed for 1-9 above individually.)

2.1.7. Further Exploration


As the dir('') list showed, there are many more operations on strings
than we have discussed, and there are further variations of the ones
above with more parameters. Methods startswith , endswith , and replace
are discussed later in More String Methods. If you want to reach a
systematic reference from inside Idle, go to Help Python Docs Library
Reference, and (for Python 3.4 at least) Section 4 Built-in Types, and then
Section 4.7.1, String Methods. (This depends on you being attached to
the Internet, or having idle configured to look at a local copy of the
official Python documentation.) Many methods use features we have not
discussed yet, but currently accessible methods are capitalize , title ,
strip , rfind , ....

[1] s.lower()
[2] Use a plus sign to concatenate the pieces.
[3] s.lower() + s.upper()
Hands-on Python Tutorial » 2. Objects and Methods » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on May 24, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 2. Objects and Methods » previous | next | index

Table Of Contents 2.2. More Classes and Methods


2.2. More Classes and
Methods The classes and methods introduced here are all used in the revised
2.2.1. Appending to a mad lib program developed in the next section.
List
2.2.2. Sets
2.2.3. Constructors

Previous topic
2.2.1. Appending to a List
2.1. Strings, Part III
Before making a version of the madlib program that is much easier to
Next topic use with new stories, we need a couple of facts about other types of
2.3. Mad Libs Revisited objects that are built into Python.

This Page So far we have used lists, but we have not changed the contents of lists.
The most obvious way to change a list is to add a new element onto the
Show Source
end. Lists have the method append . It modifies the original list. Another
Quick search word for modifiable is mutable. Lists are mutable. Most of the types of
object considered so far (int, str, float) are immutable or not mutable.
Read and see how the list named words changes:
Go

Enter search terms or a module, >>> words = list()


>>> words
class or function name.
[]
>>> words.append('animal')
>>> words
['animal']
>>> words.append('food')
>>> words
['animal', 'food']
>>> words.append('city')
>>> words
['animal', 'food', 'city']

This is particularly useful in a loop, where we can accumulate a new list.


Read the start of this simple example:

def multiplyAll(numList, multiplier):


'''Return a new list containing all of the elements of
numList,
each multiplied by multiplier. For example:

>>> print(multiplyAll([3, 1, 7], 5))


[15, 5, 35]

'''
# more to come

Clearly this will be repetitious. We will process each element of the list
numList. A for-each loop with numList is appropriate. Also we need to
create more and more elements of the new list. The accumulation
pattern will work here, with a couple of wrinkles.
Test yourself: If we are going to accumulate a list. How do we initialize
the list?

In earlier versions of the accumulation loop, we needed an assignment


statement to change the object doing the accumulating, but now the
method append modifies its list automatically, so we do not need an
assignment statement. Read and try the example program multiply1.py :

def multiplyAll(numList, multiplier): #1


'''Return a new list containing all
of the elements of numList, each
multiplied by multiplier. For example:

>>> print(multiplyAll([3, 1, 7], 5))


[15, 5, 35]
'''

newList = list() #2
for num in numList: #3
newList.append(num*multiplier) #4
return newList #5

print(multiplyAll([3, 1, 7], 5)) #6

Make sure the result makes sense to you or follow the details of playing
computer below.

Line numList multiplier newList num comment


1-5 - - - - definition
6 - - - - call function
1 [3, 1, 7] 5 - - set formal parameters
2 [3, 1, 7] 5 []
3 [3, 1, 7] 5 [] 3 first in list
4 [3, 1, 7] 5 [15] 3 append 3*5 = 15
3 [3, 1, 7] 5 [15] 1 next in list
4 [3, 1, 7] 5 [15, 5] 1 append 1*5 = 5
3 [3, 1, 7] 5 [15, 5] 7 last in list
4 [3, 1, 7] 5 [15, 5, 35] 7 append 7*5 = 35
3 [3, 1, 7] 5 [15, 5, 35] 7 done with list and loop
5 [3, 1, 7] 5 [15, 5, 35] 7 return [15, 5, 35]
6 - - - - print [15, 3, 35]

Using a for-loop and append is a powerful and flexible way to derive a


new list, but not the only way.

« 2.2.2. Sets
A list may contain duplicates, as in [2, 1, 3, 2, 5, 5, 2] . This is
sometimes useful, and sometimes not. You may have learned in math
class that a set is a collection that does not allow repetitions (a set
automatically removes repetitions suggested). Python has a type set .
Like many type names, it can be used to convert other types. In this
case it makes sense to convert any collection, and the process removes
duplicates. Read and see what happens:

>>> strList = ['z', 'zz', 'c', 'z', 'bb', 'z', 'a', 'c']
>>> aSet = set(strList)
>>> aSet
{'bb', 'zz', 'a', 'c', 'z'}

(Technically, a set is unordered, so your version of Idle may list the set in
a different order.) Set literals are enclosed in braces. Like other
collections, a set can be used as a sequence in a for loop. Read, and
check it makes sense:

>>> for s in aSet:


print(s)

bb
zz
a
c
z

Predict the result of the following, and then paste it into the Shell and
test. (You may not guess Python’s order, but see if you can get the right
length and the right elements in some order.)

set(['animal', 'food', 'animal', 'food', 'food', 'city'])

2.2.3. Constructors
We have now seen several examples of the name of a type being used
as a function. Read these earlier examples:

x = int('123')
s = str(123)
nums = list()
aSet = set(numberList)

In all such cases a new object of the specified type is constructed and
returned, and such functions are called constructors.
Hands-on Python Tutorial » 2. Objects and Methods » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on May 24, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 2. Objects and Methods » previous | next | index

Table Of Contents 2.3. Mad Libs Revisited


2.3. Mad Libs Revisited
2.3.1. A Function to
Ease the Creation of
Mad Libs 2.3.1. A Function to Ease the Creation of Mad
2.3.2. Creative Problem
Solving Steps
Libs
2.3.3. The Revised Mad
Lib Program The versions so far of the Mad Lib program have been fairly easy to edit
2.3.3.1. Substring to contain a different mad lib:
Locations Exercise
1. Come up with a new mad lib story as a format string
Previous topic 2. Produce the list of cues to prompt the user with.
2.2. More Classes and
Methods The first is a creative process. The second is a pretty mechanical
process of looking at the story string and copying out the embedded
Next topic
cues. The first is best left to humans. The second can be turned over to
2.4. Graphics
a Python function to do automatically, as many times as we like, with
This Page any story - if we write the code once.
Show Source Writing the Python code also takes a different sort of creativity! We shall
Quick search illustrate a creative process. This is a bigger problem than any we have
taken on so far. It is hard to illustrate a creative process if the overall
problem is too simple.
Go

Try and follow along. Read the sample code and pseudocode.
Enter search terms or a module,
class or function name. There is nothing to try in the Shell or editor until further notice.

If we follow the last version of the mad lib program, we had a loop
iterating through the keys in the story, and making a dictionary entry for
each key. The main idea we follow here is to use the format string to
automatically generate the sequence of keys. Let us plan this unified
task as a new function:

def getKeys(formatString):
'''formatString is a format string with embedded dictionary
keys.
Return a list containing all the keys from the format
string.'''
# more to come

The keys we want are embedded like {animal} . There may be any
number of them in the format string. This indeterminacy suggests a loop
to extract them. At this point we have only considered for loops. There
is no obvious useful sequence to iterate through in the loop. (We are
trying to create such a sequence!) The only pattern we have discussed
that does not actively process each element of a significant list is a
repeat-loop, where we just use the loop to repeat the correct number of
times. This will work in this case. (There is a more efficient approach
after we introduce While Statements, suggested in Mad Lib While
Exercise.)

First: how many times do we want to pull out a key - once for each
embedded format. So how do we count those?

The count method is obviously a way to count. However we must count a


fixed string, and the whole embedded formats vary, with different keys in
the middle. A common part is ‘{‘, and this should not appear in the
regular text of the story, so it will serve our purpose:

repetitions = formatString.count('{')
for i in range(repetitions):
...

This is certainly the most challenging code to date. Before jumping into
writing it all precisely, we can give an overall plan in pseudo-code. For a
plan we need an idea of what quantities we are keeping track of, and
name them, and outline the sequence of operations with them.

Think about data to name:

In this case we are trying to find a list. We will need to extract one
element at a time and add it to the list, so we need a list, say keyList .

The central task is to identifying the individual keys. When we find a key
we can call it key .

Think about identifying the text of individual keys. This may be too hard
to think of in the abstract, so let us use as a concrete example, and let
us keep it simple for the moment. Suppose the data in formatString
starts off as follows. The lines with numbers are added to help us refer
to the indices. Display of possible data:

# 1111111111222222222233333333
# 01234567890123456789012345678901234567
'blah {animal} blah blah {food} ...' # start of formatString

The first key is at formatString[6:12] . The next key is 'food' at


'animal'
formatString[25:29] . To identify each key as part of formatString we
need not only the variable formatString , but also index variables to
locate the start and end of the slices. Obvious names for the indices are
start and end . We want to keep them current so the next key slice will
always be

key = formatString[start : end]

Let us now put this all in an overall plan. We will have to continuously
modify the start and end indices, the key, and the list. We have a basic
pattern for accumulating a list, involving initializing it and appending to it.
We can organize a plan, partly fleshed out, with a couple of
approximations still to be worked out. The parts that are not yet in
Python are emphasized:
def getKeys(formatString):
keyList = list()
?? other initializations ??
repetitions = formatString.count('{')
for i in range(repetitions):
find the start and end of the next key
key = formatString[start : end]
keyList.append(key)
return keyList

We can see that the main piece left is to find the start and end indices for
each key. The important word is find: the method we consider is find .
As with the plan for using count above, the beginnings of keys are
identified by the specific string '{' . We can look first at

formatString.find('{')

but that is not the full solution. If we look at our concrete example, the
value returned is 5, not 6. How in general would we locate the beginning
of the slice we want?

We do not want the position of the ‘{‘, but the position just after the ‘{‘.
Since the length of ‘{‘ is 1, the correct position is 5+1 = 6. We can
generalize this to

start = formatString.find('{') + 1

OK, what about end ? Clearly it is at the '}' . In this example,

formatString.find('}')

gives us 12, exactly the right place for the end of the slice (one place
past the actual end).

There is a subtle issue here that will be even more important later: We
will keep wanting to find the next brace, and not keep finding the first
brace. How do we fix that?

Recall there was an alternate syntax for find , specifying the first place to
search! That is what we need. Where should we start? Well, the end
must come after the start of the key, our variable start :

start = formatString.find('{') + 1
end = formatString.find('}', start)

Figuring out how to find the first key is important, but we are not home
free yet. We need to come up with code that works in a loop for the later
keys. This code will not work for the next one. Why?
As written, the search for ‘{‘ will again start from the beginning of the
format string, and will find the first key again. So what code will work for
the second search? We search for the start of the next key going from
the end of the last one:

start = formatString.find('{', end) + 1


end = formatString.find('}', start)

This code will also work for later times through the loop: each time uses
the end from the previous time through the loop.

So now what do we do for finding the first key? We could separate the
treatment of the first key from all the others, but an easier approach
would be to see if we can use the same code that already works for the
later repetitions, and initialize variables right to make it work. If we are to
find the first key with

start = formatString.find('{', end) + 1

then what do we need? Clearly end needs to have a value. (There will not
be a previous loop to give it a value.) What value should we initialize it
to? The first search starts from the beginning of the string at index 0.
The initialization goes before the loop, so the full code for this function is

def getKeys(formatString):
'''formatString is a format string with embedded dictionary
keys.
Return a list containing all the keys from the format
string.'''

keyList = list()
end = 0
repetitions = formatString.count('{')
for i in range(repetitions):
start = formatString.find('{', end) + 1
end = formatString.find('}', start)
key = formatString[start : end]
keyList.append(key)

return keyList

Look the code over and see that it makes sense. See how we
continuously modify start , end , key , and keyList . Since we have coded
this new part as a function, it is easy to test without running a whole
revised mad lib program. We can just run this function on some test
data, like the original story, and see what it does. Run the example
program testGetKeys.py :

'''Test the function to extract keys from a format string for a


dictionary.'''

def getKeys(formatString):
'''formatString is a format string with embedded dictionary
keys.
Return a list containing all the keys from the format
string.'''

keyList = list()
end = 0
repetitions = formatString.count('{')
for i in range(repetitions):
start = formatString.find('{', end) + 1
end = formatString.find('}', start)
key = formatString[start : end]
keyList.append(key)
return keyList

originalStory = """
Once upon a time, deep in an ancient jungle,
there lived a {animal}. This {animal}
liked to eat {food}, but the jungle had
very little {food} to offer. One day, an
explorer found the {animal} and discovered
it liked {food}. The explorer took the
{animal} back to {city}, where it could
eat as much {food} as it wanted. However,
the {animal} became homesick, so the
explorer brought it back to the jungle,
leaving a large supply of {food}.

The End
"""

print(getKeys(originalStory))

The functions should behave as advertised.

Look back on the process described to come up with the getKeys


function. One way of approaching the creative process of coding this
function was provided. There are many other results and approaches
possible, but the discussion did illustrate a number of useful ideas which
you might adapt to other problems, in different orders and proportions,
that are summarized in the next section.

2.3.2. Creative Problem Solving Steps


Clearly define the problem. Encapsulating the problem in a function
is useful, with inputs as parameters and results returned. Include a
complete documentation string, and a clear example (or examples)
of what it is to do.
If the problem is too complicated to just solve easily, straight away,
it is often useful to construct a representative concrete case and
write down concrete steps appropriate to this problem.
Think of the data in the problem, and give names to the pieces you
will need to refer to. Clearly identify the ideas that the names
correspond to. When using sequences like lists or strings, you
generally need names not only for the whole collection, but also
parts like items and characters or substrings, and often indices that
locate parts of the collection.
Plan the overall approach to the problem using a mixture of Python
and suggestive phrases (called pseudo-code). The idea is to refine
« it to a place where you can fairly easily figure how to replace the
phrases with Python.
Replace your pseudo-code parts with Python. If you had a concrete
example to guide you, you may want to test with one of more
further concrete examples with different specific data, to make
sure you come up with code for a generalization that works in all
cases. This is the process of abstraction.
Recognize where something is being repeated over and over, and
think how to structure appropriate loops. Can you incorporate any
patterns you have seen before?
If you need to create a successive modification loop, think of how to
approach the first repetition and then how to modify the data for
the later times through the loop. Usually you can make the first
time through the loop fit the more general pattern needed for the
repetitions by making appropriate initializations before the loop.
Check and test your code, and correct as necessary.

2.3.3. The Revised Mad Lib Program


There is still an issue for use of getKeys in the mad lib program: the
returned list has unwanted repetitions in it. We can easily create a
collection without repetitions, how?

One approach is to make a set from the list returned. A neater approach
would be to just have the getKeys function return a set in the first place.
We need to slightly change to getKeys ‘ documentation string and the
final return line. This will be included in a new version of the mad lib
program, which makes it easy to substitute a new story. We will make
the story’s format string be a parameter to the central method, tellStory .
We will also put the clearly identified step of filling the dictionary with the
user’s picks in a separate function. We will test tellStory with the
original story. Note the changes included in madlib2.py and run:

"""
madlib2.py
Interactive display of a mad lib, which is provided as a Python
format string,
with all the cues being dictionary formats, in the form {cue}.

In this version, the cues are extracted from the story


automatically,
and the user is prompted for the replacements.

Original verison adapted from code of Kirby Urner


"""

def getKeys(formatString):
'''formatString is a format string with embedded dictionary
keys.
Return a set containing all the keys from the format
string.'''

keyList = list()
end = 0
repetitions = formatString.count('{')
for i in range(repetitions):
start = formatString.find('{', end) + 1 # pass the '{'
end = formatString.find('}', start)
key = formatString[start : end]
keyList.append(key) # may add duplicates

return set(keyList) # removes duplicates: no duplicates in a


set

def addPick(cue, dictionary): # from madlibDict.py


'''Prompt for a user response using the cue string,
and place the cue-response pair in the dictionary.
'''
promptFormat = "Enter a specific example for {name}: "
prompt = promptFormat.format(name=cue)
response = input(prompt)
dictionary[cue] = response

def getUserPicks(cues):
'''Loop through the collection of cue keys and get user
choices.
Return the resulting dictionary.
'''
userPicks = dict()
for cue in cues:
addPick(cue, userPicks)
return userPicks

def tellStory(storyFormat):
'''storyFormat is a string with Python dictionary references
embedded,
in the form {cue}. Prompt the user for the mad lib
substitutions
and then print the resulting story with the substitutions.
'''
cues = getKeys(storyFormat)
userPicks = getUserPicks(cues)
story = storyFormat.format(**userPicks)
print(story)

def main():
originalStoryFormat = '''
Once upon a time, deep in an ancient jungle,
there lived a {animal}. This {animal}
liked to eat {food}, but the jungle had
very little {food} to offer. One day, an
explorer found the {animal} and discovered
it liked {food}. The explorer took the
{animal} back to {city}, where it could
eat as much {food} as it wanted. However,
the {animal} became homesick, so the
explorer brought it back to the jungle,
leaving a large supply of {food}.

The End
'''
tellStory(originalStoryFormat)
input("Press Enter to end the program.")

main()

Does the use of well-named functions make it easier to follow this code?
We have broken the large overall problem into many smaller steps.
Make sure you follow the flow of execution and data and see how all the
pieces fit together.

After Python file manipulation is introduced, in Mad Lib File Exercise, you
can modify the program to work on a madlib format string chosen by the
user and taken from a file.

2.3.3.1. Substring Locations Exercise


* Rename the example file locationsStub.py to be locations.py , and
complete the function printLocations , to print the index of each location
in the string s where target is located. For example,

printLocations('This is a dish', 'is')

would go through the string 'This is a dish' looking for the index of
places where 'is' appears, and would print:

2
5
11

Similarly

printLocations('This is a dish', 'h')

would print:

1
13

The program stub already uses the string method count . You will need to
add code using the more general form of find .
Hands-on Python Tutorial » 2. Objects and Methods » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on May 24, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 2. Objects and Methods » previous | next | index

Table Of Contents 2.4. Graphics


2.4. Graphics
2.4.1. A Graphics Graphics make programming more fun for many people. To fully
Introduction introduce graphics would involve many ideas that would be a distraction
2.4.2. Sample Graphics
now. This section introduces a simplified graphics module developed by
Programs
2.4.3. A Windows John Zelle for use with his Python Programming book. My slight
Operating System elaboration of his package is graphics.py in the example programs.
Specialization: .pyw
2.4.4. Graphics.py vs.
Event Driven Graphics
Warning: It is particularly important in Microsoft Windows with the
2.4.5. The graphics not to open Idle from the Start menu. Graphics will fail. Use
Documentation for one of the following two methods.
graphics.py
2.4.5.1. Scene
Exercise Warning: To work on the most systems, this version of graphics.py
2.4.5.2. Changing cannot be used from the Idle shell. There is an issue with the use of
Scene Exercise multiple threads of execution. The video for this revised section was
2.4.6. Issues with uploaded Aug 17, 2012: Any earlier version is completely out of date.
Mutable Objects
2.4.7. More on Mutable
and Immutable Types In Microsoft Windows, have Python version 3.4 or greater and be sure to
2.4.8. Animation start Idle in one of two ways:
2.4.8.1. Nose in
Face Exercise from the shortcut provided in the examples folder (in the same
2.4.8.2. Faces folder as graphics.py )
Exercise
or start by right clicking on an existing graphics program file in the
2.4.8.3. Moving
Faces Exercise same folder to get a context menu and choose Open With Idle:.
2.4.9. Entry Objects
2.4.10. Color Names
2.4.11. Custom Colors
2.4.12. Random Colors 2.4.1. A Graphics Introduction
2.4.12.1. Ranges
Exercise Note: You will just be a user of the graphics.py code, so you do not
2.4.12.2. Text need to understand the inner workings! It uses all sorts of features of
Triangle Exercise
Python that are way beyond these tutorials. There is no particular
Previous topic need to open graphics.py in the Idle editor.
2.3. Mad Libs Revisited
Load into Idle and start running example graphIntroSteps.py , or start
Next topic
running from the operating system folder. Each time you press return,
2.5. Files
look at the screen and read the explanation for the next line(s).
This Page
Press return:
Show Source

Quick search from graphics import *


win = GraphWin()

Go
Zelle’s graphics are not a part of the standard Python distribution. For the
Python interpreter to find Zelle’s module, it must be imported. The first
Enter search terms or a module, line above makes all the types of object of Zelle’s module accessible, as
class or function name. if they were already defined like built-in types str or list .

Look around on your screen, and possibly underneath other windows:


There should be a new window labeled “Graphics Window”, created by
the second line. Bring it to the top, and preferably drag it around to make
it visible beside your Shell window. A GraphWin is a type of object from
Zelle’s graphics package that automatically displays a window when it is
created. The assignment statement remembers the window object as
win for future reference. (This will be our standard name for our graphics
window object.) A small window, 200 by 200 pixels is created. A pixel is
the smallest little square that can by displayed on your screen. Modern
screen usually have more than 1000 pixels across the whole screen.

Press return:

pt = Point(100, 50)

This creates a Point object and assigns it the name pt . Unlike when a
GraphWin is created, nothing is immediately displayed: In theory you
could have more than one GraphWin . Zelle designed the graphics module
so you must tell Python into which GraphWin to draw the Point . A Point
object, like each of the graphical objects that can be drawn on a
GraphWin , has a method [1] draw .

Press return:

pt.draw(win)

Now you should see the Point if you look hard in the Graphics Window -
it shows as a single, small, black pixel. Graphics windows have a
Cartesian (x,y) coordinate system. The dimensions are initially
measured in pixels. The first coordinate is the horizontal coordinate,
measured from left to right, so 100 is about half way across the 200 pixel
wide window. The second coordinate, for the vertical direction, increases
going down from the top of the window by default, not up as you are
likely to expect from geometry or algebra class. The coordinate 50 out of
the total 200 vertically should be about 1/4 of the way down from the top.
We will see later that we can reorient the coordinate system to fit our
taste.

Henceforth you will see a draw method call after each object is created,
so there is something to see.

Press return:

cir = Circle(pt, 25)


cir.draw(win)

The first line creates a Circle object with center at the previously defined
pt and with radius 25. This object is remembered with the name cir . As
with all graphics objects that may be drawn within a GraphWin , it is only
made visible by explicitly using its draw method.

So far, everything has been drawn in the default color black. Graphics
objects like a Circle have methods to change their colors. Basic color
name strings are recognized. You can choose the color for the circle
outline as well as filling in the inside.

Press return:

cir.setOutline('red')
cir.setFill('blue')

Note the method names. They can be used with other kinds of Graphics
objects, too. (We delay a discussion of fancier colors until Color Names
and Custom Colors.)

Press return:

line = Line(pt, Point(150, 100))


line.draw(win)

A Line object is constructed with two Points as parameters. In this case


we use the previously named Point, pt , and specify another Point
directly. Technically the Line object is a segment between the the two
points.

Warning: In Python (150, 100) is a tuple , not a Point . To make a


Point , you must use the full constructor: Point(150, 100) . Point s, not
tuple s, must be used in the constructors for all graphics objects.

A rectangle is also specified by two points. The points must be diagonally


opposite corners.

Press return:

rect = Rectangle(Point(20, 10), pt)


rect.draw(win)

In this simple system, a Rectangle is restricted to have horizontal and


vertical sides. A Polygon , introduced in the next section, is used for all
more general straight-sided shapes.

You can move objects around in a GraphWin. Shortly this will be handy
for animation. The parameters to the move method are the amount to
shift the x and y coordinates. See if you can guess the result before you
press return:
line.move(10, 40)

Did you remember that the y coordinate increases down the screen?

Take your last look at the Graphics Window, and make sure that all the
steps make sense. Then destroy the window win with the GraphWin
method close .

Press return:

win.close()

The example program graphIntro.py starts with the same graphics code
as graphIntoSteps.py , but without the need for pressing returns.

An addition I have made to Zelle’s package is the ability to print a string


value of graphics objects for debugging purposes. If some graphics
object isn’t visible because it is underneath something else of off the
screen, temporarily adding this sort of output might be a good reality
check.

At the end of graphIntro.py , I added print lines to illustrate the debugging


possibilites:

print('cir:', cir)
print('line:', line)
print('rect:', rect)

You can load graphIntro.py into Idle, run it, and add further lines to
experiment if you like. Of course you will not see their effect until you run
the whole program!

2.4.2. Sample Graphics Programs


In graphIntro.py , a prompt to end the graphics program appeared in the
Shell window, requiring you to pay attention to two windows. Instead
consider a very simple example program, face.py , where all the action
takes place in the graphics window. The only interaction is to click the
mouse to close the graphics window. In Windows, have a directory
window open to the Python examples folder containing face.py , where
your operating system setup may allow you be just double click on the
icon for face.py to run it. If that does not work on your system, you can
always run from inside Idle.

After you have checked out the picture, click with the mouse inside the
picture, as requested, to terminate the program.

After you have run the program, you can examine the program in Idle or
look below. The whole program is shown first; smaller pieces of it are
discussed later:

'''A simple graphics example constructs a face from basic shapes.


'''

from graphics import *

def main():
win = GraphWin('Face', 200, 150) # give title and dimensions
win.yUp() # make right side up coordinates!

head = Circle(Point(40,100), 25) # set center and radius


head.setFill("yellow")
head.draw(win)

eye1 = Circle(Point(30, 105), 5)


eye1.setFill('blue')
eye1.draw(win)

eye2 = Line(Point(45, 105), Point(55, 105)) # set endpoints


eye2.setWidth(3)
eye2.draw(win)

mouth = Oval(Point(30, 90), Point(50, 85)) # set corners of


bounding box
mouth.setFill("red")
mouth.draw(win)

label = Text(Point(100, 120), 'A face')


label.draw(win)

message = Text(Point(win.getWidth()/2, 20), 'Click anywhere


to quit.')
message.draw(win)
win.getMouse()
win.close()

main()

Let us look at individual parts.

Until further notice the set-off code is for you to read and have explained.

from graphics import *

Immediately after the documentation string, always have the import line
in your graphics program, to allow easy access to the graphics.py
module.

win = GraphWin('Face', 200, 150) # give title and dimensions


win.yUp() # make right side up coordinates!

The first line shows the more general parameters for constructing a new
GraphWin, a window title plus width and height in pixels. The second
line shows how to turn the coordinate system right-side-up, so the y
coordinate increases up the screen, using the yUp method. (This is one
of my additions to Zelle’s graphics.) Thereafter, all coordinates are given
in the new coordinate system. All the lines of code up to this point in the
program are my standard graphics program starting lines (other than the
specific values for the title and dimensions). You will likely start your
programs with similar code.

head = Circle(Point(40,100), 25) # set center and radius


head.setFill('yellow')
head.draw(win)

eye1 = Circle(Point(30, 105), 5)


eye1.setFill('blue')
eye1.draw(win)

The lines above create two circles, in each case specifying the centers
directly. They are filled in and made visible. Also note, that because the
earlier win.yUp call put the coordinates in the normal orientation, the y
coordinate, 100 and 105, are above the middle of the 150 pixel high
window.

eye2 = Line(Point(45, 105), Point(55, 105)) # set endpoints


eye2.setWidth(3)
eye2.draw(win)

The code above draws and displays a line, and illustrates another
method available to graphics object, setWidth , making a thicker line.

mouth = Oval(Point(30, 90), Point(50, 85)) # set corners of


bounding box
mouth.setFill('red')
mouth.draw(win)

The code above illustrates another kind of graphics object, an Oval (or
ellipse). There are several ways an oval could be specified. Zelle chose
to have you specify the corners of the bounding box that is just as high
and as wide as the oval. This rectangle is only imagined, not actually
drawn. (If you want to see such a rectangle, create a Rectangle object
with the same two Points as parameters....)

label = Text(Point(100, 120), 'A face')


label.draw(win)

The code above illustrates how a Text object is used to place text on the
window. The parameters to construct the Text object are the point at the
center of the text, and the text string itself.

The exact coordinates for the parts were determined by a number of trial-
and-error refinements to the program. An advantage of graphics is that
you can see the results of your programming, and make changes if you
do not like the results!
The final action is to have the user signal to close the window. Just as
with waiting for keyboard input from input , it is important to prompt the
user before waiting for a response! In a GraphWin, that means using
prompt must be made with a Text object displayed explicitly before the
response is expected.

message = Text(Point(win.getWidth()/2, 20), 'Click anywhere to


quit.')
message.draw(win)
win.getMouse()
win.close()

The new addition to the Text parameters here is win.getWidth() to obtain


the window width. (There is also a win.getHeight() .) Using
win.getWidth()/2 means the horizontal position is set up to be centered,
half way across the window’s width.

After the first two lines draw the prompting text, the line win.getMouse()
waits for a mouse click. In this program, the position of the mouse click
is not important. (In the next example the position of this mouse click will
be used.) As you have seen before, win.close() closes the graphics
window.

While our earlier text-based Python programs have automatically


terminated after the last line finishes executing, that is not true for
programs that create new windows: The graphics window must be
explicitly closed. The win.close() is necessary.

We will generally want to prompt the user to finally close the graphics
window. Because such a sequence is so common, I have added another
method for Zelle’s GraphWin objects, promptClose , so the last four lines
can be reduced to

win.promptClose(win.getWidth()/2, 20)

where the only specific data needed is the coordinates of the center of
the prompt.

The modified program is in face2.py . You can copy the form of this
program for other simple programs that just draw a picture. The size and
title on the window will change, as well as the specific graphical objects,
positions, and colors. Something like the last line can be used to
terminate the program.

Warning: If you write a program with a bug, and the program bombs
out while there is a GraphWin on the screen, a dead GraphWin
lingers. The best way to clean things up is to make the Shell window
be the current window and select from the menu Shell Restart Shell.
Another simple drawing example is balloons.py . Feel free to run it and
look at the code in Idle. Note that the steps for the creation of all three
balloons are identical, except for the location of the center of each
balloon, so a loop over a list of the centers makes sense.

The next example, triangle.py , illustrates similar starting and ending


code. In addition it explicitly interacts with the user. Rather than the code
specifying literal coordinates for all graphical objects, the program
remembers the places where the user clicks the mouse, and uses them
as the vertices of a triangle.

Return to the directory window for the Python examples. In Windows you
can double click on the icon for triangle.py to run it.

While running the program, follow the prompts in the graphics window
and click with the mouse as requested.

After you have run the program, you can examine the program in Idle or
look below:

'''Program: triangle.py or triangle.pyw (best name for Windows)


Interactive graphics program to draw a triangle,
with prompts in a Text object and feedback via mouse clicks.
'''

from graphics import *

def main():
win = GraphWin('Draw a Triangle', 350, 350)
win.yUp() # right side up coordinates
win.setBackground('yellow')
message = Text(Point(win.getWidth()/2, 30), 'Click on three
points')
message.setTextColor('red')
message.setStyle('italic')
message.setSize(20)
message.draw(win)

# Get and draw three vertices of triangle


p1 = win.getMouse()
p1.draw(win)
p2 = win.getMouse()
p2.draw(win)
p3 = win.getMouse()
p3.draw(win)
vertices = [p1, p2, p3]

# Use Polygon object to draw the triangle


triangle = Polygon(vertices)
triangle.setFill('gray')
triangle.setOutline('cyan')
triangle.setWidth(4) # width of boundary line
triangle.draw(win)

message.setText('Click anywhere to quit') # change text


message
win.getMouse()
win.close()
main()

Let us look at individual parts.

The lines before the new iine:

win.setBackground('yellow')

are standard starting lines (except for the specific values chosen for the
width, height, and title). The background color is a property of the whole
graphics window that you can set.

message = Text(Point(win.getWidth()/2, 20), 'Click on three


points')
message.setTextColor('red')
message.setStyle('italic')
message.setSize(20)
message.draw(win)

Again a Text object is created. This is the prompt for user action. These
lines illustrate most of the ways the appearance of a Text object may be
modified, with results like in most word processors. The reference pages
for graphics.py give the details. This reference is introduced shortly in
The Documentation for graphics.py.

After the prompt, the program looks for a response:

# Get and draw three vertices of triangle


p1 = win.getMouse()
p1.draw(win)

p2 = win.getMouse()
p2.draw(win)

p3 = win.getMouse()
p3.draw(win)

The win.getMouse() method (with no parameters), waits for you to click


the mouse inside win . Then the Point where the mouse was clicked is
returned. In this code three mouse clicks are waited for, remembered in
variables p1 , p2 , and p3 , and the points are drawn.

Next we introduce a very versatile type of graphical object, a Polygon ,


which may have any number of vertices specified in a list as its
parameter. We see that the methods setFill and setOutline that we
used earlier on a Circle , and the setWidth method we used for a Line ,
also apply to a Polygon , (and also to other graphics objects).

vertices = [p1, p2, p3]


triangle = Polygon(vertices)
triangle.setFill('gray')
triangle.setOutline('cyan')
triangle.setWidth(4)
triangle.draw(win)

Besides changing the style of a Text object, the text itself may be
changed:

message.setText('Click anywhere to quit')

Then lines responding to this prompt:

win.getMouse()
win.close()

If you want to use an existing Text object to display the quitting prompt,
as I did here, I provide a variation on my window closing method that
could replace the last three lines:

win.promptClose(message)

An existing Text object may be given as parameter rather than


coordinates for a new text object. The complete code with that
substitution is in triangle2.py .

If you want to make regular polygons or stars, you need some


trigonometry, not required for this tutorial, but you can see its use in
example polygons.py .

2.4.3. A Windows Operating System


Specialization: .pyw
This Windows-specific section is not essential. It does describe how to
make some Windows graphical programs run with less clutter.

If you ran the triangle.py program by double clicking its icon under
Windows, you might have noticed a console window first appearing,
followed by the graphics window. For this program, there was no
keyboard input or screen output through the console window, so the
console window was unused and unnecessary. In such cases, under
Windows, you can change the source file extension from .py to .pyw,
suppressing the display of the console window. If you are using
Windows, change the filename triangle.py to tringle.pyw , double click
on the icon in its directory folder, and check it out.

The distinction is irrelevant inside Idle, which always has its Shell
window.

2.4.4. Graphics.py vs. Event Driven Graphics


This optional section only looks forward to more elaborate graphics
systems than are used in this tutorial.

One limitation of the graphics.py module is that it is not robust if a


graphics window is closed by clicking on the standard operating system
close button on the title bar. If you close a graphics window that way,
you are likely to get a Python error message. On the other hand, if your
program creates a graphics window and then terminates abnormally due
to some other error, the graphics window may be left orphaned. In this
case the close button on the title bar is important: it is the easiest
method to clean up and get rid of the window!

This lack of robustness is tied to the simplification designed into the


graphics module. Modern graphics environments are event driven. The
program can be interrupted by input from many sources including mouse
clicks and key presses. This style of programming has a considerable
learning curve. In Zelle’s graphics package, the complexities of the
event driven model are pretty well hidden. If the programmer wants user
input, only one type can be specified at a time (either a mouse click in
the graphics window via the getMouse method, or via the input
keyboard entry methods into the Shell window).

2.4.5. The Documentation for graphics.py

Thus far various parts of Zelle’s graphics package have been introduced
by example. A systematic reference to Zelle’s graphics package with the
form of all function calls is at
http://mcsp.wartburg.edu/zelle/python/graphics/graphics/index.html. We
have introduced most of the important concepts and methods.

One special graphics input object type, Entry , will be discussed later.
You might skip it for now. Another section of the reference that will not
be pursued in the tutorials is the Image class.

Meanwhile you can look at


http://mcsp.wartburg.edu/zelle/python/graphics/graphics/index.html. It is
important to pay attention to the organization of the reference: Most
graphics object share a number of common methods. Those methods
are described together, first. Then, under the headings for specific types,
only the specialized additional methods are discussed.

The version for this Tutorial has a few elaborations. Here is all their
documentation together:

GraphWin method yUp (y increases upward)


When you first create a GraphWin, the y coordinates increase down
the screen. To reverse to the normal orientation use my GraphWin
yUp method.

win = Graphwin('Right side up', 300, 400)


win.yUp()

GraphWin method promptClose (Prompt and Close Graphics


Window)
You generally want to continue displaying your graphics window until
the user chooses to have it closed. The GraphWin promptClose
method posts a prompt, waits for a mouse click, and closes the
GraphWin. There are two ways to call it, depending on whether you
want to use an existing Text object, or just specify a location for the
center of the prompt.

win.promptClose(win.getWidth()/2, 30) # specify x, y


coordinates of prompt

or

msg = Text(Point(100, 50), 'Original message...')


msg.draw(win)
# ...
# ... just important that there is a drawn Text object
win.promptClose(msg) # use existing Text object

String Representations of all Graphics Object Types


Each graphical type can be converted to a string or printed, and a
descriptive string is produced (for debugging purposes). It only
shows position, not other parts of the state of the object.

>>> pt = Point(30, 50)


>>> print(pt)
Point(30, 50)
>>> ln = Line(pt, Point(100, 150))
>>> print(ln)
Line(Point(30, 50), Point(100, 150))

2.4.5.1. Scene Exercise


Make a program scene.py creating a scene with the graphics methods.
You are likely to need to adjust the positions of objects by trial and error
until you get the positions you want. Make sure you have graphics.py in
the same directory as your program.

2.4.5.2. Changing Scene Exercise


Elaborate the scene program above so it becomes changeScene.py , and
changes one or more times when you click the mouse (and use
win.getMouse() ). You may use the position of the mouse click to affect
the result, or it may just indicate you are ready to go on to the next view.
2.4.6. Issues with Mutable Objects
Zelle chose to have the constructor for a Rectangle take diagonally
opposite corner points as parameters. Suppose you prefer to specify
only one corner and also specify the width and height of the rectangle.
You might come up with the following function, makeRect , to return such a
new Rectangle. Read the following attempt:

def makeRect(corner, width, height):


'''Return a new Rectangle given one corner Point and the
dimensions.'''
corner2 = corner
corner2.move(width, height)
return Rectangle(corner, corner2)

The second corner must be created to use in the Rectangle constructor,


and it is done above in two steps. Start corner2 from the given corner
and shift it by the dimensions of the Rectangle to the other corner. With
both corners specified, you can use Zelle’s version of the Rectangle
constructor.

Unfortunately this is an incorrect argument. Run the example program


makeRectBad.py :

'''Program: makeRectBad.py
Attempt a function makeRect (incorrectly),
which takes a takes a corner point and dimensions to construct a
Rectangle.
'''

from graphics import *

def makeRect(corner, width, height): # Incorrect!


'''Return a new Rectangle given one corner Point and the
dimensions.'''
corner2 = corner
corner2.move(width, height)
return Rectangle(corner, corner2)

def main():
win = GraphWin('Draw a Rectangle (NOT!)', 300, 300)
win.yUp()

rect = makeRect(Point(20, 50), 250, 200)


rect.draw(win)

win.promptClose(win.getWidth()/2, 20)

main()

By stated design, this program should draw a rectangle with one corner
at the point (20,50) and the other corner at (20+250,50+200) or the point
(270,250), and so the rectangle should take up most of the 300 by 300
window. When you run it however that is not what you see. Look
carefully. You should just see one Point toward the upper right corner,
where the second corner should be. Since a Rectangle was being drawn,
it looks like it is the tiniest of Rectangle s, where the opposite corners are
at the same point! Hm, well the program did make the corners be the
same initially. Recall we set

corner2 = corner

What happens after that?

Read and follow the details of what happens.

We need to take a much more careful look at what naming an object


means. A good way to visualize this association between a name and an
object is to draw an arrow from the name to the object associated with it.
The object here is a Point , which has an x and y coordinate describing
its state, so when the makeRect method is started the parameter name
corner is associated with the actual parameter, a Point with coordinates
(20, 50).

Next, the assignment statement associates the name corner2 with the
same object. It is another name, or alias, for the original Point .

The next line,

corner2.move(width, height)

internally changes or mutates the Point object, and since in this case
width is 250 and height is 200, the coordinates of the Point associated
with the name corner2 change to 20+250=270 and 50+200=250:
Look! The name corner is still associated with the same object, but that
object has changed internally! That is the problem: we wanted to keep
the name corner associated with the point with original coordinates, but
it has been modified.

The solution is to use the clone method that is defined for all the
graphical objects in graphics.py. It creates a separate object, which is a
copy with an equivalent state. We just need to change the line

corner2 = corner

to

corner2 = corner.clone()

A diagram of the situation after the cloning is:

Though corner and corner2 refer to points with equivalent coordinates,


they do not refer to the same object. Then after

corner2.move(width, height)

we get:
No conflict: corner and corner2 refer to the corners we want. Run the
corrected example program, makeRectangle.py .

2.4.7. More on Mutable and Immutable Types


Read this section if you want a deeper understanding of the significance
of mutable and immutable objects.

This alias problem only came up because a Point is mutable. We had no


such problems with the immutable types int or str .

Read and follow the discussion of the following code.

Just for comparison, consider the corresponding diagrams for code with
int s that looks superficially similar:

a = 2
b = a
b = b + 3

After the first two lines we have an alias again:

The third line does not change the int object 2. The result of the addition
operation refers to a different object, 5, and the name b is assigned to it:
Hence a is still associated with the integer 2 - no conflict.

It is not technically correct to think of b as being the number 2, and then


5, but a little sloppiness of thought does not get you in trouble with
immutable types. With mutable types, however, be very careful of
aliases. Then it is very important to remember the indirectness: that a
name is not the same thing as the object it refers to.

Another mutable type is list . A list can be cloned with the slice notation:
[:]. Try the following in the Shell: [2]

nums = [1, 2, 3]
numsAlias = nums
numsClone = nums[:]
nums.append(4)
numsAlias.append(5)
nums
numsAlias
numsClone

2.4.8. Animation
Run the example program, backAndForth0.py. The whole program is
shown below for convenience. Then each individual new part of the
code is discussed individually:

'''Test animation and depth.


'''

from graphics import *


import time

def main():
win = GraphWin('Back and Forth', 300, 300)
win.yUp() # make right side up coordinates!

rect = Rectangle(Point(200, 90), Point(220, 100))


rect.setFill("blue")
rect.draw(win)

cir1 = Circle(Point(40,100), 25)


cir1.setFill("yellow")
cir1.draw(win)
cir2 = Circle(Point(150,125), 25)
cir2.setFill("red")
cir2.draw(win)

for i in range(46):
cir1.move(5, 0)
time.sleep(.05)

for i in range(46):
cir1.move(-5, 0)
time.sleep(.05)

win.promptClose(win.getWidth()/2, 20)

main()

Read the discussion below of pieces of the code from the program
above. Do not try to execute fragments alone.

There are both an old and a new form of import statement:

from graphics import *


import time

The program uses a function from the time module. The syntax used for
the time module is actually the safer and more typical way to import a
module. As you will see later in the program, the sleep function used
from the time module will be referenced as time.sleep() . This tells the
Python interpreter to look in the time module for the sleep function.

If we had used the import statement

from time import *

then the sleep function could just be referenced with sleep() . This is
obviously easier, but it obscures the fact that the sleep function is not a
part of the current module. Also several modules that a program imports
might have functions with the same name. With the individual module
name prefix, there is no ambiguity. Hence the form import moduleName is
actually safer than from moduleName import * .

You might think that all modules could avoid using any of the same
function names with a bit of planning. To get an idea of the magnitude of
the issue, have a look at the number of modules available to Python. Try
the following in the in the Shell (and likely wait a number of seconds):

help('modules')

Without module names to separate things out, it would be very hard to


totally avoid name collisions with the enormous number of modules you
see displayed, that are all available to Python!
Back to the current example program: The main program starts with
standard window creation, and then makes three objects:

rect = Rectangle(Point(200, 90), Point(220, 100))


rect.setFill('blue')
rect.draw(win)

cir1 = Circle(Point(40,100), 25)


cir1.setFill('yellow')
cir1.draw(win)

cir2 = Circle(Point(150,125), 25)


cir2.setFill('red')
cir2.draw(win)

Zelle’s reference pages do not mention the fact that the order in which
these object are first drawn is significant. If objects overlap, the ones
which used the draw method later appear on top. Other object methods
like setFill or move do not alter which are in front of which. This
becomes significant when cir1 moves. The moving cir1 goes over the
rectangle and behind cir2 . (Run the program again if you missed that.)

The animation starts with the code for a simple repeat loop:

for i in range(46): # animate cir1 to the right


cir1.move(5, 0)
time.sleep(.05)

This very simple loop animates cir1 moving in a straight line to the right.
As in a movie, the illusion of continuous motion is given by jumping only
a short distance each time (increasing the horizontal coordinate by 5).
The time.sleep function, mentioned earlier, takes as parameter a time in
seconds to have the program sleep, or delay, before continuing with the
iteration of the loop. This delay is important, because modern computers
are so fast, that the intermediate motion would be invisible without the
delay. The delay can be given as a decimal, to allow the time to be a
fraction of a second.

The next three lines are almost identical to the previous lines, and move
the circle to the left (-5 in the horizontal coordinate each time).

for i in range(46): # animate cir1 to the left


cir1.move(-5, 0)
time.sleep(.05)

The next example program, backAndForth1.py, it just a slight variation,


looking to the user just like the last version. Only the small changes are
shown below. This version was written after noticing how similar the two
animation loops are, suggesting an improvement to the program:
Animating any object to move in a straight line is a logical abstraction
well expressed via a function.
The loop in the initial version of the program contained a number of
arbitrarily chosen constants, which make sense to turn into parameters.
Also, the object to be animated does not need to be cir1 , it can be any
of the drawable objects in the graphics package. The name shape is
used to make this a parameter:

def moveOnLine(shape, dx, dy, repetitions, delay):


for i in range(repetitions):
shape.move(dx, dy)
time.sleep(delay)

Then in the main function the two similar animation loops are reduced to
a line for each direction:

moveOnLine(cir1, 5, 0, 46, .05)


moveOnLine(cir1, -5, 0, 46, .05)

Make sure you see these two lines with function calls behave the same
way as the two animation loops in the main program of the original
version.

Run the next example version, backAndForth2.py. The changes are


more substantial here, and the display of the whole program is followed
by display and discussion of the individual changes:

'''Test animation of a group of objects making a face.


'''

from graphics import *


import time

def moveAll(shapeList, dx, dy):


''' Move all shapes in shapeList by (dx, dy).'''
for shape in shapeList:
shape.move(dx, dy)

def moveAllOnLine(shapeList, dx, dy, repetitions, delay):


'''Animate the shapes in shapeList along a line.
Move by (dx, dy) each time.
Repeat the specified number of repetitions.
Have the specified delay (in seconds) after each repeat.
'''
for i in range(repetitions):
moveAll(shapeList, dx, dy)
time.sleep(delay)

def main():
win = GraphWin('Back and Forth', 300, 300)
win.yUp() # make right side up coordinates!

rect = Rectangle(Point(200, 90), Point(220, 100))


rect.setFill("blue")
rect.draw(win)

head = Circle(Point(40,100), 25)


head.setFill("yellow")
head.draw(win)

eye1 = Circle(Point(30, 105), 5)


eye1.setFill('blue')
eye1.draw(win)

eye2 = Line(Point(45, 105), Point(55, 105))


eye2.setWidth(3)
eye2.draw(win)

mouth = Oval(Point(30, 90), Point(50, 85))


mouth.setFill("red")
mouth.draw(win)

faceList = [head, eye1, eye2, mouth]

cir2 = Circle(Point(150,125), 25)


cir2.setFill("red")
cir2.draw(win)

moveAllOnLine(faceList, 5, 0, 46, .05)


moveAllOnLine(faceList, -5, 0, 46, .05)

win.promptClose(win.getWidth()/2, 20)

main()

Read the following discussion of program parts.

Moving a single elementary shape is rather limiting. It is much more


interesting to compose a more complicated combination, like the face
from the earlier example face.py . To animate such a combination, you
cannot use the old moveOnLine function, because we want all the parts to
move together, not one eye all the way across the screen and then have
the other eye catch up! A variation on moveOnLine is needed where all the
parts move together. We need all the parts of the face to move one step,
sleep, and all move again, .... This could all be coded in a single
method, but there are really two ideas here:

1. Moving a group of objects one step.


2. Animating a number of moves for the group.

This suggests two functions. Another issue is how to handle a group of


elementary graphics objects. The most basic combination of objects in
Python is a list , so we assume a parameter shapeList , which is a list
of elementary graphics objects. For the first function, moveAll, just move
all the objects in the list one step. Since we assume a list of objects and
we want to move each, this suggests a for-each loop:

def moveAll(shapeList, dx, dy):


''' Move all shapes in shapeList by (dx, dy).'''
for shape in shapeList:
shape.move(dx, dy)

Having this function, we can easily write the second function


moveAllOnLine moveOnLine
, with a simple change from the function,
substituting the moveAll function for the line with the move method:

«
def moveAllOnLine(shapeList, dx, dy, repetitions, delay):
'''Animate the shapes in shapeList along a line.
Move by (dx, dy) each time.
Repeat the specified number of repetitions.
Have the specified delay (in seconds) after each repeat.
'''
for i in range(repetitions):
moveAll(shapeList, dx, dy)
time.sleep(delay)

The code in main to construct the face is the same as in the earlier
example face.py . Once all the pieces are constructed and colored, they
must be placed in a list, for use in moveAllOnLine :

faceList = [head, eye1, eye2, mouth]

Then, later, the animation uses the faceList to make the face go back
and forth:

moveAllOnLine(faceList, 5, 0, 46, .05)


moveAllOnLine(faceList, -5, 0, 46, .05)

This version of the program has encapsulated and generalized the


moving and animating by creating functions and adding parameters that
can be substituted. Again, make sure you see how the functions
communicate to make the whole program work. This is an important and
non-trivial use of functions.

In fact all parts of the face do not actually move at once: The moveAll
loop code moves each part of the face separately, in sequence.
Depending on your computer setup, all the parts of the face may appear
to move together. Again, the computer is much faster than our eyes. On
a computer that repaints the screen fast enough, the only images we
notice are the ones on the screen when the animation is sleeping.

Note: On a fast enough computer you can make many consecutive


changes to an image before the next sleep statement, and they all
appear to happen at once in the animation.

Optional refinement: Not all computers are set up for the same graphics
speed in Python. One machine that I use animates backAndForth2.py
quite well. Another seems to have the mouth wiggle. On the latter sort of
machine, during animation it is useful not to have visible screen changes
for every individual move. Instead you can explicitly tell the computer
when it is the right time to redraw the screen. The computer can store
changes and then flush them to the screen. Withholding updates is
controlled by win.autoflush . It starts as True, but can be changed to
False before animation. When set to False, you must call win.flush()
every time you want the screen refreshed. That is going to be just before
the time.sleep() in an animation. In backAndForth2Flush.py this is
illustrated, with moveAllOnLine replaced by moveAllOnLineFlush :

#NEW Flush version with win


parameter
def moveAllOnLineFlush(shapeList, dx, dy, repetitions, delay,
win):
'''Animate the shapes in shapeList along a line in win.
Move by (dx, dy) each time.
Repeat the specified number of repetitions.
Have the specified delay (in seconds) after each repeat.
'''
win.autoflush = False # NEW: set before animation
for i in range(repetitions):
moveAll(shapeList, dx, dy)
win.flush() # NEW needed to make all the changes
appear
time.sleep(delay)
win.autoflush = True # NEW: set after animation

Run the next example program backAndForth3.py .

The final version, backAndForth3.py and its variant, backAndForth3Flush.py ,


use the observation that the code to make a face embodies one unified
idea, suggesting encapsulation inside a function. Once you have
encapsulated the code to make a face, we can make several faces!
Then the problem with the original code for the face is that all the
positions for the facial elements are hard-coded: The face can only be
drawn in one position. The full listing of backAndForth3.py below includes
a makeFace function with a parameter for the position of the center of the
face.

Beneath the listing of the whole program is a discussion of the individual


changes:

'''Test animation of a group of objects making a face.


Combine the face elements in a function, and use it twice.
Have an extra level of repetition in the animation.

This version may be wobbly and slow on some machines:


Then see backAndForthFlush.py.
'''

from graphics import *


import time

def moveAll(shapeList, dx, dy):


''' Move all shapes in shapeList by (dx, dy).'''
for shape in shapeList:
shape.move(dx, dy)
def moveAllOnLine(shapeList, dx, dy, repetitions, delay):
'''Animate the shapes in shapeList along a line.
Move by (dx, dy) each time.
Repeat the specified number of repetitions.
Have the specified delay (in seconds) after each repeat.
'''
for i in range(repetitions):
moveAll(shapeList, dx, dy)
time.sleep(delay)

def makeFace(center, win):


'''display face centered at center in window win.
Return a list of the shapes in the face.
'''

head = Circle(center, 25)


head.setFill("yellow")
head.draw(win)

eye1Center = center.clone() # face positions are relative to


the center
eye1Center.move(-10, 5) # locate further points in
relation to others
eye1 = Circle(eye1Center, 5)
eye1.setFill('blue')
eye1.draw(win)

eye2End1 = eye1Center.clone()
eye2End1.move(15, 0)
eye2End2 = eye2End1.clone()
eye2End2.move(10, 0)

eye2 = Line(eye2End1, eye2End2)


eye2.setWidth(3)
eye2.draw(win)

mouthCorner1 = center.clone()
mouthCorner1.move(-10, -10)
mouthCorner2 = mouthCorner1.clone()
mouthCorner2.move(20, -5)

mouth = Oval(mouthCorner1, mouthCorner2)


mouth.setFill("red")
mouth.draw(win)

return [head, eye1, eye2, mouth]

def main():
win = GraphWin('Back and Forth', 300, 300)
win.yUp() # make right side up coordinates!

rect = Rectangle(Point(200, 90), Point(220, 100))


rect.setFill("blue")
rect.draw(win)

faceList = makeFace(Point(40, 100), win)


faceList2 = makeFace(Point(150,125), win)

stepsAcross = 46
dx = 5
dy = 3
wait = .05
for i in range(3):
moveAllOnLine(faceList, dx, 0, stepsAcross, wait)
moveAllOnLine(faceList, -dx, dy, stepsAcross//2, wait)
moveAllOnLine(faceList, -dx, -dy, stepsAcross//2, wait)

win.promptClose(win.getWidth()/2, 20)

main()

Read the following discussion of program parts.

As mentioned above, the face construction function allows a parameter


to specify where the center of the face is. The other parameter is the
GraphWin that will contain the face.

def makeFace(center, win):

then the head is easily drawn, using this center , rather than the previous
cir1 with its specific center point (40, 100):

head = Circle(center, 25)


head.setFill('yellow')
head.draw(win)

For the remaining Point s used in the construction there is the issue of
keeping the right relation to the center. This is accomplished much as in
the creation of the second corner point in the makeRectangle function in
Section Issues with Mutable Objects. A clone of the original center Point
is made, and then moved by the difference in the positions of the
originally specified Point s. For instance, in the original face, the center
of the head and first eye were at (40, 110) and (30, 115) respectively.
That means a shift between the two coordinates of (-10, 5), since 30-40
= -10 and 130-110 = 20.

eye1Center = center.clone() # face positions are relative to the


center
eye1Center.move(-10, 5) # locate further points in relation
to others
eye1 = Circle(eye1Center, 5)
eye1.setFill('blue')
eye1.draw(win)

The only other changes to the face are similar, cloning and moving
Point s, rather than specifying them with explicit coordinates.

eye2End1 = eye1Center.clone()
eye2End1.move(15, 0)
eye2End2 = eye2End1.clone()
eye2End2.move(10, 0)
eye2 = Line(eye2End1, eye2End2)
eye2.setWidth(3)
eye2.draw(win)

mouthCorner1 = center.clone()
mouthCorner1.move(-10, -10)
mouthCorner2 = mouthCorner1.clone()
mouthCorner2.move(20, -5)
mouth = Oval(mouthCorner1, mouthCorner2)
mouth.setFill('red')
mouth.draw(win)

Finally, the list of elements for the face must be returned to the caller:

return [head, eye1, eye2, mouth]

Then in the main function, the program creates a face in exactly the
same place as before, but using the makeFace function, with the original
center of the face Point(40, 100) . Now with the makeFace function, with its
center parameter, it is also easy to replace the old cir2 with a whole
face!

faceList = makeFace(Point(40, 100), win)


faceList2 = makeFace(Point(150,125), win)

The animation section is considerably elaborated in this version.

stepsAcross = 46
dx = 5
dy = 3
wait = .01
for i in range(3):
moveAllOnLine(faceList, dx, 0, stepsAcross, wait)
moveAllOnLine(faceList, -dx, dy, stepsAcross//2, wait)
moveAllOnLine(faceList, -dx, -dy, stepsAcross//2, wait)

The unidentified numeric literals that were used before are replaced by
named values that easily identify the meaning of each one. This also
allows the numerical values to be stated only once, allowing easy
modification.

The whole animation is repeated three times by the use of a simple


repeat loop.

The animations in the loop body illustrate that the straight line of motion
does not need to be horizontal. The second and third lines use a non-
zero value of both dx and dy for the steps, and move diagonally.

Make sure you see now how the whole program works together,
including all the parameters for the moves in the loop.

By the way, the documentation of the functions in a module you have just
run in the Shell is directly available. Try in the Shell:

help(moveAll)

2.4.8.1. Nose in Face Exercise


* Save backAndForth3.py or backAndForth3Flush.py to the new name
backAndForth4.py . Add a triangular nose in the middle of the face in the
makeFace function. Like the other features of the face, make sure the
position of the nose is relative to the center parameter. Make sure the
nose is included in the final list of elements of the face that get returned!

2.4.8.2. Faces Exercise


* Make a program faces.py that asks the user to click the mouse, and
then draws a face at the point where the user clicked. Copy the makeFace
function definition from the previous exercise, and use it! Elaborate this
with a Simple Repeat Loop, so this is repeated six times: After each of 6
mouse clicks, a face immediately appears at the location of the latest
click. Think how you can reuse your code each time through the loop!

2.4.8.3. Moving Faces Exercise


* Animate two faces moving in different directions at the same time in a
program move2Faces.py . You cannot use the moveAllOnLine function. You
will have to make a variation of your own. You can use the moveAll
function separately for each face. Hint: imagine the old way of making
an animated cartoon. If each face was on a separate piece of paper, and
you wanted to animate them moving together, you would place them
separately, record one frame, move them each a bit toward each other,
record another frame, move each another bit toward each other, record
another frame, .... In our animations “record a frame” is replaced by a
short sleep to make the position visible to the user. Make a loop to
incorporate the repetition of the moves.

2.4.9. Entry Objects


Read this section if you want to allow the user to enter text directly into a
graphics window.

When using a graphics window, the shell window is still available.


Keyboard input can be done in the normal text fashion, waiting for a
response, and going on after the user presses the Enter key. It is
annoying to make a user pay attention to two windows, so the graphics
module provides a way to enter text inside a graphics window, with the
Entry type. The entry is a partial replacement for the input function.

Run the simple example, greet.py, which is copied below:

"""Simple example with Entry objects.


Enter your name, click the mouse, and see greetings.
"""

from graphics import *

def main():
win = GraphWin("Greeting", 300, 300)
win.yUp()

instructions = Text(Point(win.getWidth()/2, 40),


"Enter your name.\nThen click the mouse.")
instructions.draw(win)

entry1 = Entry(Point(win.getWidth()/2, 200),10)


entry1.draw(win)

Text(Point(win.getWidth()/2, 230),'Name:').draw(win) # label


for the Entry

win.getMouse() # To know the user is finished with the text.

name = entry1.getText()

greeting1 = 'Hello, ' + name + '!'


Text(Point(win.getWidth()/3, 150), greeting1).draw(win)

greeting2 = 'Bonjour, ' + name + '!'


Text(Point(2*win.getWidth()/3, 100), greeting2).draw(win)

win.promptClose(instructions)

main()

The only part of this with new ideas is:

entry1 = Entry(Point(win.getWidth()/2, 200),10)


entry1.draw(win)
Text(Point(win.getWidth()/2, 230),'Name:').draw(win) # label for
the Entry

win.getMouse() # To know the user is finished with the text.


name = entry1.getText()

The first line of this excerpt creates an Entry object, supplying its center
point and a number of characters to leave space for (10 in this case).

As with other places where input is requested, a separate static label is


added.

The way the underlying events are hidden in graphics.py, there is no


signal when the user is done entering text in an Entry box. To signal the
program, a mouse press is used above. In this case the location of the
mouse press is not relevant, but once the mouse press is processed,
execution can go on and read the Entry text. The method name getText
is the same as that used with a Text object.

Run the next example, addEntries.py, also copied below:

"""Example with two Entry objects and type conversion.


Do addition.
"""

from graphics import *

def main():
win = GraphWin("Addition", 300, 300)
win.yUp()

instructions = Text(Point(win.getWidth()/2, 30),


"Enter two numbers.\nThen click the mouse.")
instructions.draw(win)

entry1 = Entry(Point(win.getWidth()/2, 250),25)


entry1.setText('0')
entry1.draw(win)

Text(Point(win.getWidth()/2, 280),'First Number:').draw(win)

entry2 = Entry(Point(win.getWidth()/2, 180),25)


entry2.setText('0')
entry2.draw(win)

Text(Point(win.getWidth()/2, 210),'Second Number:').draw(win)

win.getMouse() # To know the user is finished with the text.

numStr1 = entry1.getText()
num1 = int(numStr1)

numStr2 = entry2.getText()
num2 = int(numStr2)
sum = num1 + num2

result = "The sum of\n{num1}\nplus\n{num2}\nis


{sum}.".format(**locals())
Text(Point(win.getWidth()/2, 110), result).draw(win)

win.promptClose(instructions)

main()

As with the input statement, you only can read strings from an Entry .
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.

def makeLabeledEntry(entryCenterPt, entryWidth, initialStr,


labelText, win):
'''Return an Entry object with specified center, width in
characters, and
initial string value. Also create a static label over it
with
specified text. Draw everything in the GraphWin win.
'''

entry = Entry(entryCenterPt, entryWidth)


entry.setText(initialStr)
entry.draw(win)
labelCenter = entryCenterPt.clone()
labelCenter.move(0, 30)
Text(labelCenter,labelText).draw(win)
return entry

In case I want to make more Entries with labels later, and refer to this
code again, I put some extra effort in, making things be parameters even
if only one value is used in this program. The position of the label is
made 30 units above the entry by using the clone and move methods.
Only the Entry is returned, on the assumption that the label is static, and
once it is drawn, I can forget about it. Since I do not refer later to the
Text object, I do not bother to name it, but just draw it immediately.

Then the corresponding change in the main function is just two calls to
this function:

entry1 = makeLabeledEntry(Point(win.getWidth()/2, 250), 25,


'0', 'First Number:', win)
entry2 = makeLabeledEntry(Point(win.getWidth()/2, 180), 25,
'0', 'Second Number:', win)

These lines illustrate that a statement may take more than one line. In
particular, as in the Shell, Python is smart enough to realize that there
must be a continuation line if the parentheses do not match.

While I was improving things, I also changed the conversions to integers.


In the first version I wanted to emphasize the existence of both the string
and integer data as a teaching point, but the num1Str and num2Str
variables were only used once, so a more concise way to read and
convert the values is to eliminate them:

num1 = int(entry1.getText())
num2 = int(entry2.getText())
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 also the ability to draw
with custom colors.

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.
"""
from graphics import *
import random, time

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)

radius = random.randrange(3, 40)


x = random.randrange(5, 295)
y = random.randrange(5, 295)

circle = Circle(Point(x,y), radius)


circle.setFill(color)
circle.draw(win)
time.sleep(.05)

win.promptClose(win.getWidth()/2, 20)

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

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)

circle = Circle(Point(x,y), radius)

What are the smallest and largest values I allow for x and y? [3]

Random values are often useful in games.

2.4.12.1. Ranges Exercise


Write a program ranges.py in three parts. (Test after each added part.)

This problem is not a graphics program. It is just a regular text program


to illustrate your understanding of ranges and loops.

For simplicity each of the requested number sequences can just be


printed with one number per line. Print a label for each number
sequence before you print the sequence, like Numbers 1-4 , Numbers 1-n ,
Five random numbers in 1-n .

1. First use the range function and a for-loop to produce the sequence
1, 2, 3, 4, and then print the numbers, one number per line.
2. Prompt the user to input an integer n and print the sequence 1, 2,
3, ... , n - including n, using a for-loop. Hint: [4]
3. Use a Simple Repeat Loop to find and print five randomly chosen
numbers from the range 1, 2, 3, ... , n. Use the same value of n
that the user chose earlier in the program. It should be possible
that the number n is printed sometimes.

2.4.12.2. Text Triangle Exercise


* Write a program texttriangle.py .

This, too, is not a graphics program. Prompt the user for a small positive
integer value, that I’ll call n . Then use a for-loop with a range function
call to make a triangular arrangement of ‘#’characters, with n ‘#’
characters in the last line. Hint: [5]

Then leave a blank line. Then make a similar triangle, except start with
the line with n ‘#’ characters. To make the second triangle, you can use
a for-loop of the form discussed so far, but that is trickier than looking
ahead to The Most General range Function and using a for-loop where a
range function call has a negative step size.

Here is the screen after a posible run with user input 4:

Enter a small positive integer: 4


#
##
###
####

####
###
##
#

And another possible run with user input 2:

Enter a small positive integer: 2


#
##

##
#

[1] The basic ideas of objects and methods were introduced in Object
Orientation.
[2] Actually, lists are even trickier, because the elements of a list are
arbitrary: There can still be issues of dependence between the
original and cloned list if an element of the list is itself mutable,
and you choose to mutate the element.
[3] 5 and 294 (one less than 295).
[4] If 4 or n is the last number, what is the first number past the end of
the sequence?
[5] A row of ‘#’ characters is easiest if you remember the string
multiplication operator *.
Hands-on Python Tutorial » 2. Objects and Methods » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Sep 08, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 2. Objects and Methods » previous | next | index

Table Of Contents 2.5. Files


2.5. Files
2.5.1. Writing Files This section fits here logically (as an important built-in type of object) but
2.5.2. Reading Files it is not needed for the next chapter, More On Flow of Control.
2.5.2.1. PrintUpper
Exercise
Thus far you have been able to save programs, but anything produced
2.5.2.2. Mad Lib
File Exercise during the execution of a program has been lost when the program
ends. Data has not persisted past the end of execution. Just as
Previous topic programs live on in files, you can generate and read data files in Python
2.4. Graphics that persist after your program has finished running.
Next topic As far as Python is concerned, a file is just a string (often very large!)
2.6. Summary stored on your file system, that you can read or write, gradually or all
together.
This Page
Show Source

Quick search 2.5.1. Writing Files


Open a directory window for your Python program directory. First note
Go
that there is no file named sample.txt.
Enter search terms or a module,
Make sure you have started Idle so the current directory is your Python
class or function name.
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()

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.

Warning: If the file already existed, the old contents are destroyed.

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 be actually written to the file yet: Since
initiating a file operation is thousands of times slower than memory
operations, Python buffers data, saving small amounts and writing a
larger chunk all at once.

Warning: 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.

2.5.2. Reading Files


Run the example program printFile.py , shown below:

'''Quick illustration of reading a file.


(needs revisedFile.py run first to create sample3.txt)
'''

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. [1]

Note: There are three related but distinct concepts related to files.
Beginners often try to merge several in their head or substitute one for
another:

1. The file name is a string that identifies the file in your computer’s
file system.
2. You need the file name to open a file creating a file object, but the
file object (that I tend to call inFile or outFile ) is not the same
«
as the name of the file on your hard drive.
3. There is still one more step to the most important part, the
contents of the file. The read and write methods for a file object
read existing content or write new content.

2.5.2.1. PrintUpper Exercise


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.
c. * copyFileUpper.py: modify fileUpper.py to write the upper case
contents 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 .

2.5.2.2. Mad Lib File Exercise


Write madlib3.py , a small modification of madlib2.py , requiring only a
modification to the main function of madlib2.py . (Even better is to start
from madlib2a.py if you did the exercise in Unique List Exercise). Also
create a file myMadlib.py , as described below.

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 as an example
of the story file format expected. 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 myMadlib.py , and your
new madlib story file.

[1] If, for some reason, you want to reread this same file while the
same program is running, you need to close it and reopen it.
Hands-on Python Tutorial » 2. Objects and Methods » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 25, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 2. Objects and Methods » previous | next | index

Previous topic 2.6. Summary


2.5. Files
The same typographical conventions will be used as in Summary.
Next topic
3. More On Flow of Control 1. Object notation
This Page 1. When the name of a type of object is used as a function call,
Show Source it is called a constructor, and a new object of that type is
constructed and implicitly returned (no return statement). The
Quick search meanings of any parameters to the constructor depend on
the type. [Constructors]
Go
2. object .methodName( parameters )

Enter search terms or a module,


class or function name. Objects 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. [Object Orientation]

2. String ( str ) indexing and methods

See Summary for string literals and symbolic string operations.

1. String Indexing. [String Indices]

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.

2. String Slices [String Slices]

stringReference [ start : pastEnd ]


stringReference [ : pastEnd ]
stringReference [ start : ]
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.

3. String Methods: Assume s refers to a string

1. s .upper()

Returns an uppercase version of the string s. [Object


Orientation]

2. s .lower()

Returns a lowercase version of the string s. [Object


Orientation]

3. s .count( sub )

Returns the number of repetitions of the substring sub


inside s. [Object Orientation]

4. 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. [Object Orientation]

5. 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. [split]

6. 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. [join]

7. Further string methods are discussed in the Python


Reference Manual, Section 2.3.6.1, String Methods.
[Further Exploration]

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.
[Sets]

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. [Appending to a List]

5. Files [Files]

1. open( nameInFileSystem )
open( nameInFileSystem , 'r' )

returns a file object for reading, where nameInFileSystem


must be a string referring to an existing file.

2. open( nameInFileSystem , 'w')

returns a file object for writing, where the string


nameInFileSystem will be the name of the file. If it did not
exist before, it is created. If it did exist before, all previous
contents are erased.

3. If infile is a file opened for reading, and outfile is a file opened


for writing, then

infile .read()

returns the entire file contents of the file as a


string.

infile .close()

closes the file in the operating system (generally


not needed, unless the file is going to be modified
later, while your program is still running).

outfile .write( stringExpression )

writes the string to the file, with no extra newline.

outfile .close()

closes the file in the operating system (important


to make sure the whole file gets written and to
allow other access to the file).

6. Mutable objects [Issues with Mutable Objects] Care must be taken


whenever a second name is assigned to a mutable object. It is an
alias for the original name, and refers to the exact same object. A
mutating method applied to either name changes the one object
referred to by both names. 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.

1. Introductory examples of using graphics.py are in [A Graphics


Introduction], [Sample Graphics Programs], and [Entry
Objects]

2. 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. [A Windows Operating System Specialization: .pyw]

3. Event-driven programs

Graphical programs are typically event-driven, meaning the


next operation done by the program 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. [Graphics.py vs.
Event Driven Graphics]

4. Custom computer colors are expressed in terms of the


amounts of red, green, and blue. [Custom Colors]

5. See also Animation under the summary of Programming


Techniques.

8. Additional programming techniques


These techniques extend those listed in the summary of the
previous chapter. [Summary]

1. Sophisticated operations with substrings require careful


setting of variables used as an index. [Index Variables]

2. There are a number of techniques to assist creative


programming, including pseudo-code and gradual
generalization from concrete examples. [Creative Problem
Solving Steps]

3. Animation: a loop involving small moves followed by a short


delay (assumes the time module is imported): [Animation]

loop heading :
move all objects a small step in the proper
direction
time.sleep( delay )

4. Example of a practical successive modification loop: [A


Function to Ease the Creation of Mad Libs]

5. Examples of encapsulating ideas in functions and reusing


them: [A Function to Ease the Creation of Mad Libs], [The
Revised Mad Lib Program], [Animation]

6. Random results can be introduced into a program using the


random module. [Random Colors]
Hands-on Python Tutorial » 2. Objects and Methods » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » previous | next | index

Previous topic 3. More On Flow of Control


2.6. Summary
You have varied the normal forward sequence of operations with
Next topic functions and for loops. To have full power over your programs, you
3.1. If Statements
need two more constructions that changing the flow of control: decisions
This Page choosing between alternatives ( if statements), and more general loops
that are not required to be controlled by the elements of a collection
Show Source
( while loops).
Quick search
3.1. If Statements
3.1.1. Simple Conditions
Go
3.1.2. Simple if Statements
3.1.3. if - else Statements
Enter search terms or a module,
class or function name. 3.1.4. More Conditional Expressions
3.1.4.1. Graduate Exercise
3.1.4.2. Head or Tails Exercise
3.1.4.3. Strange Function Exercise
3.1.5. Multiple Tests and if - elif Statements
3.1.5.1. Sign Exercise
3.1.5.2. Grade Exercise
3.1.5.3. Wages Exercise
3.1.6. Nesting Control-Flow Statements
3.1.6.1. Short String Exercise
3.1.6.2. Even Print Exercise
3.1.6.3. Even List Exercise
3.1.6.4. Unique List Exercise
3.1.7. Compound Boolean Expressions
3.1.7.1. Congress Exercise
3.1.8. More String Methods
3.1.8.1. Article Start Exercise
3.1.8.2. Is Number String Exercise
3.2. Loops and Tuples
3.2.1. Exercises
3.2.1.1. Choose Button Exercise
3.3. While Statements
3.3.1. Simple while Loops
3.3.2. The Most General range Function
« 3.3.3. Interactive while Loops
3.3.3.1. Interactive Sum Exercise
3.3.3.2. Safe Number Input Exercise
3.3.3.3. Savings Exercise
3.3.3.4. Strange Sequence Exercise
3.3.4. Graphical Applications
3.3.4.1. Exercise Moving Undraw
3.3.4.2. Make Path Exercise
3.3.4.3. Random Start Exercise
3.3.4.4. Mad Lib While Exercise
3.3.4.5. Find Hole Game Exercise
3.3.5. Fancier Animation Loop Logic (Optional)
3.4. Arbitrary Types Treated As Boolean
3.5. Further Topics to Consider
3.6. Summary

Hands-on Python Tutorial » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on May 24, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 3. More On Flow of Control » previous | next | index

Table Of Contents 3.1. If Statements


3.1. If Statements
3.1.1. Simple Conditions
3.1.2. Simple if
Statements 3.1.1. Simple Conditions
3.1.3. if - else
Statements The statements introduced in this chapter will involve tests or conditions.
3.1.4. More Conditional More syntax for conditions will be introduced later, but for now consider
Expressions simple arithmetic comparisons that directly translate from math into
3.1.4.1. Graduate
Python. Try each line separately in the Shell
Exercise
3.1.4.2. Head or
Tails Exercise 2 < 5
3 > 7
3.1.4.3. Strange
x = 11
Function Exercise x > 10
3.1.5. Multiple Tests 2 * x < x
and if - elif type(True)
Statements
3.1.5.1. Sign
Exercise
You see that conditions are either True or False (with no quotes!). These
3.1.5.2. Grade are the only possible Boolean values (named after 19th century
Exercise mathematician George Boole). In Python the name Boolean is
3.1.5.3. Wages shortened to the type bool . It is the type of the results of true-false
Exercise
conditions or tests.
3.1.6. Nesting Control-
Flow Statements
3.1.6.1. Short
String Exercise
3.1.6.2. Even Print
3.1.2. Simple if Statements
Exercise
3.1.6.3. Even List Run this example program, suitcase.py. Try it at least twice, with inputs:
Exercise 30 and then 55. As you an see, you get an extra result, depending on
3.1.6.4. Unique List the input. The main code is:
Exercise
3.1.7. Compound weight = float(input("How many pounds does your suitcase
Boolean Expressions weigh? "))
3.1.7.1. Congress if weight > 50:
Exercise print("There is a $25 charge for luggage that heavy.")
3.1.8. More String print("Thank you for your business.")
Methods
3.1.8.1. Article The middle two line are an if statement. It reads pretty much like
Start Exercise
3.1.8.2. Is Number English. If it is true that the weight is greater than 50, then print the
String Exercise 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
Previous topic luggage charge. In any event, when you have finished with the if
3. More On Flow of Control statement (whether it actually does anything or not), go on to the next
Next topic statement that is not indented under the if . In this case that is the
3.2. Loops and Tuples statement printing “Thank you”.

This Page The general Python syntax for a simple if statement is


Show Source
if condition :
Quick search indentedStatementBlock

If the condition is true, then do the indented statements. If the condition is


Go not true, then skip the indented statements.

Enter search terms or a module, Another fragment as an example:


class or function name.
if balance < 0:
transfer = -balance
# transfer enough from the backup account:
backupAccount = backupAccount - transfer
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.

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
and then 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 “otherwise” 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 if - else syntax is

if condition :
indentedStatementBlockForTrueCondition
else:
indentedStatementBlockForFalseCondition

These statement blocks can have any number of statements, and can
include about any kind of statement.

See Graduate Exercise

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.

Math
Meaning Symbol Python Symbols
<
Less than <
>
Greater than >
Less than or equal ≤ <=

Greater than or equal ≥ >=


==
Equals =
Not equal ≠ !=

There should not be space between the two-symbol Python substitutes.

Notice that the obvious choice for equals, a single equal sign, is not used
to check for equality. An annoying second equal sign is required. This is
because the single equal sign is already used for assignment in Python,
so it is not available for tests.

Warning: It is a common error to use only one equal sign when you
mean to test for equality, and not make an assignment!

Tests for equality do not make an assignment, and they do not require a
variable on the left. Any expressions can be tested for equality or
inequality (!=). They do not need to be numbers! Predict the results and
try each line in the Shell:

x = 5
x
x == 5
x == 6
x
x != 6
x = 6
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]

Following up on the discussion of the inexactness of float arithmetic in


String Formats for Float Precision, 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 40 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 (String Formats for Float Precision) 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.
'''
if totalHours <= 40:
totalWages = hourlyWage*totalHours
else:
overtime = totalHours - 40
totalWages = hourlyWage*40 + (1.5*hourlyWage)*overtime
return totalWages

def main():
hours = float(input('Enter hours worked: '))
wage = float(input('Enter dollars paid per hour: '))
total = calcWeeklyWages(hours, wage)
print('Wages for {hours} hours at ${wage:.2f} per hour 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 .

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.4.1. Graduate Exercise


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 120 credits are needed for
graduation.)

3.1.4.2. Head or Tails Exercise


Write a program headstails.py . It should include a function flip() , that
simulates a single flip of a coin: It randomly prints either Heads or Tails .
Accomplish this by choosing 0 or 1 arbitrarily with random.randrange(2) ,
and use an if - else statement to print Heads when the result is 0, and
Tails otherwise.

In your main program have a simple repeat loop that calls flip() 10
times to test it, so you generate a random sequence of 10 Heads and
Tails .
3.1.4.3. Strange Function Exercise
Save the example program jumpFuncStub.py as jumpFunc.py , and complete
the definitions of functions jump and main as described in the function
documentation strings in the program. In the jump function definition use
an if - else statement (hint [2]). In the main function definition use a for -
each loop, the range function, and the jump function.

The jump function is introduced for use in Strange Sequence Exercise,


and others after that.

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 distracting. 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 - elif - else statement is indicated in


general below:

if condition1 :
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 .

See Grade Exercise.

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.5.1. Sign Exercise


Write a program sign.py to ask the user for a number. Print out which
category the number is in: 'positive' , 'negative' , or 'zero' .

3.1.5.2. Grade Exercise


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? [3]

Be sure to run your new version and test with different inputs that test all
the different paths through the program.

3.1.5.3. Wages Exercise


* 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 .

3.1.6. Nesting Control-Flow Statements


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] . You want to
process a list, so that suggests a for -each loop,

for num in numberList:

but a for -each loop runs the same code body for each element of the
list, and we only want

print(num)

for some of them. That seems like a major obstacle, but think closer at
what needs to happen concretely. 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. 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)

printAllPositive([3, -5, 2, -1, 0, 7])

This idea of nesting if statements enormously expands the possibilities


with loops. Now different things can be done at different times in loops,
as long as there is a consistent test to allow a choice between the
alternatives. Shortly, while loops will also be introduced, and you will
see if statements nested inside of them, too.

The rest of this section deals with graphical examples.

Run example program bounce1.py . It has a red ball moving and bouncing
obliquely off the edges. If you watch several times, you should see that it
starts from random locations. Also you can repeat the program from the
Shell prompt after you have run the script. For instance, right after
running the program, try in the Shell

bounceBall(-3, 1)

The parameters give the amount the shape moves in each animation
step. You can try other values in the Shell, preferably with magnitudes
less than 10.

For the remainder of the description of this example, read the extracted
text pieces.

The animations before this were totally scripted, saying exactly how
many moves in which direction, but in this case the direction of motion
changes with every bounce. The program has a graphic object shape
and the central animation step is

shape.move(dx, dy)

but in this case, dx and dy have to change when the ball gets to a
boundary. For instance, imagine the ball getting to the left side as it is
moving to the left and up. The bounce obviously alters the horizontal
part of the motion, in fact reversing it, but the ball would still continue up.
The reversal of the horizontal part of the motion means that the
horizontal shift changes direction and therefore its sign:

dx = -dx

but dy does not need to change. This switch does not happen at each
animation step, but only when the ball reaches the edge of the window.
It happens only some of the time - suggesting an if statement. Still the
condition must be determined. Suppose the center of the ball has
coordinates (x, y). When x reaches some particular x coordinate, call it
xLow, the ball should bounce.

The edge of the window is at coordinate 0, but xLow should not be 0, or


the ball would be half way off the screen before bouncing! For the edge
of the ball to hit the edge of the screen, the x coordinate of the center
must be the length of the radius away, so actually xLow is the radius of
the ball.

Animation goes quickly in small steps, so I cheat. I allow the ball to take
one (small, quick) step past where it really should go ( xLow ), and then we
reverse it so it comes back to where it belongs. In particular

if x < xLow:
dx = -dx

There are similar bounding variables xHigh , yLow and yHigh , all the radius
away from the actual edge coordinates, and similar conditions to test for
a bounce off each possible edge. Note that whichever edge is hit, one
coordinate, either dx or dy, reverses. One way the collection of tests
could be written is

if x < xLow:
dx = -dx
if x > xHigh:
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)
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

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)

def getRandomPoint(xLow, xHigh, yLow, yHigh):


'''Return a random Point with coordinates in the range
specified.'''
x = random.randrange(xLow, xHigh+1)
y = random.randrange(yLow, yHigh+1)
return Point(x, y)

def makeDisk(center, radius, win):


'''return a red disk that is drawn in win with given center
and radius.'''
disk = Circle(center, radius)
disk.setOutline("red")
disk.setFill("red")
disk.draw(win)
return disk

def bounceBall(dx, dy):


'''Make a ball bounce around the screen, initially moving by
(dx, dy)
at each jump.'''
win = GraphWin('Ball Bounce', 290, 290)
win.yUp()

radius = 10
xLow = radius # center is separated from the wall by the
radius at a bounce
xHigh = win.getWidth() - radius
yLow = radius
yHigh = win.getHeight() - radius

center = getRandomPoint(xLow, xHigh, yLow, yHigh)


ball = makeDisk(center, radius, win)

bounceInBox(ball, dx, dy, xLow, xHigh, yLow, yHigh)


win.close()

bounceBall(3, 5)

3.1.6.1. Short String Exercise


Write a program short.py with a function printShort with heading:

def printShort(strings):
'''Given a list of strings,
print the ones with at most three characters.
>>> printShort(['a', 'long', one'])
a
one
'''

In your main program, test the function, calling it several times with
different lists of strings. Hint: Find the length of each string with the len
function.

The function documentation here models a common approach:


illustrating the behavior of the function with a Python Shell interaction.
This begins with a line starting with >>> . Other exercises and examples
will also document behavior in the Shell.

3.1.6.2. Even Print Exercise


Write a program even1.py with a function printEven with heading:

def printEven(nums):
'''Given a list of integers nums,
print the even ones.

>>> printEven([4, 1, 3, 2, 7])


4
2
'''

In your main program, test the function, calling it several times with
different lists of integers. Hint: A number is even if its remainder, when
dividing by 2, is 0.

3.1.6.3. Even List Exercise


Write a program even2.py with a function chooseEven with heading:

def chooseEven(nums):
'''Given a list of integers, nums,
return a list containing only the even ones.

>>> chooseEven([4, 1, 3, 2, 7])


[4, 2]
'''

In your main program, test the function, calling it several times with
different lists of integers and printing the results. Hint: Create a new list,
and append the appropriate numbers to it.

3.1.6.4. Unique List Exercise


* The madlib2.py program has its getKeys function, which first generates a
list of each occurrence of a cue in the story format. This gives the cues
in order, but likely includes repetitions. The original version of getKeys
uses a quick method to remove duplicates, forming a set from the list.
There is a disadvantage in the conversion, though: Sets are not ordered,
so when you iterate through the resulting set, the order of the cues will
likely bear no resemblance to the order they first appeared in the list.
That issue motivates this problem:

Copy madlib2.py to madlib2a.py , and add a function with this heading:

def uniqueList(aList):
''' Return a new list that includes the first occurrence of
each value
in aList, and omits later repeats. The returned list should
« include
the first occurrences of values in aList in their original
order.

>>> vals = ['cat', 'dog', 'cat', 'bug', 'dog', 'ant', 'dog',


'bug']
>>> uniqueList(vals)
['cat', 'dog', 'bug', 'ant']
'''

A useful Boolean operator is in , checking membership in a sequence:


>>> vals = ['this', 'is', 'it]
>>> 'is' in vals
True
>>> 'was' in vals
False

It can also be used with not , as not in , to mean the opposite:

>>> vals = ['this', 'is', 'it]


>>> 'is' not in vals
False
>>> 'was' not in vals
True

In general the two versions are:

item in sequence
item not in sequence

Hint: Process aList in order. Use the new syntax to only append
elements to a new list that are not already in the new list.

After perfecting the uniqueList function, replace the last line of getKeys ,
so it uses uniqueList to remove duplicates in keyList .

Check that your madlib2a.py prompts you for cue values in the order that
the cues first appear in the madlib format string.

3.1.7. Compound Boolean Expressions


To be eligible to graduate from Loyola University Chicago, you must
have 120 credits and a GPA of at least 2.0. This translates directly into
Python as a compound condition:

credits >= 120 and GPA >=2.0

This is true if both credits >= 120 is true and GPA >= 2.0 is true. A short
example program using this would be:

credits = float(input('How many units of credit do you have? '))


GPA = float(input('What is your GPA? '))
if credits >= 120 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


The compound condition is true if both of the component conditions are
true. It is false if at least one of the conditions is false.

See Congress Exercise.

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

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.

Warning: When translating a problem stated in English using “or”, be


careful to determine whether the meaning matches Python’s or .

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 Point s. 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 pt1 and pt2 . 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 pt1 , point ,


and pt2 , 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. Also this is 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

Note: In general you should not need an if - else statement to choose


between true and false values! Operate directly on the boolean
expression.

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

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: [4]

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 function 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.'''

from graphics import *

def isBetween(x, end1, end2):


'''Return True if x is between the ends or equal to either.
The ends do not need to be in increasing order.'''

return end1 <= x <= end2 or end2 <= x <= end1

def isInside(point, rect):


'''Return True if the point is inside the Rectangle rect.'''

pt1 = rect.getP1()
pt2 = rect.getP2()
return isBetween(point.getX(), pt1.getX(), pt2.getX()) and \
isBetween(point.getY(), pt1.getY(), pt2.getY())

def makeColoredRect(corner, width, height, color, win):


''' Return a Rectangle drawn in win with the upper left
corner
and color specified.'''

corner2 = corner.clone()
corner2.move(width, -height)
rect = Rectangle(corner, corner2)
rect.setFill(color)
rect.draw(win)
return rect

def main():
win = GraphWin('pick Colors', 400, 400)
win.yUp() # right side up coordinates

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)

house = makeColoredRect(Point(60, 200), 180, 150, 'gray',


win)
door = makeColoredRect(Point(90, 150), 40, 100, 'white', win)
roof = Polygon(Point(50, 200), Point(250, 200), Point(150,
300))
roof.setFill('black')
roof.draw(win)

msg = Text(Point(win.getWidth()/2, 375),'Click to choose a


house color.')
msg.draw(win)
pt = win.getMouse()

if isInside(pt, redButton):
color = 'red'
elif isInside(pt, yellowButton):
color = 'yellow'
elif isInside(pt, blueButton):
color = 'blue'
else :
color = 'white'
house.setFill(color)

msg.setText('Click to choose a door color.')


pt = win.getMouse()

if isInside(pt, redButton):
color = 'red'
elif isInside(pt, yellowButton):
color = 'yellow'
elif isInside(pt, blueButton):
color = 'blue'
else :
color = 'white'
door.setFill(color)

win.promptClose(msg)

main()

The only further new feature used is in the long return statement in
isInside .

return isBetween(point.getX(), pt1.getX(), pt2.getX()) and \


isBetween(point.getY(), pt1.getY(), pt2.getY())

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). Extra
parentheses here would not hurt, so an alternative would be

return (isBetween(point.getX(), pt1.getX(), pt2.getX()) and


isBetween(point.getY(), pt1.getY(), pt2.getY()) )
The chooseButton1.py program is long partly because of repeated code.
The next section gives another version involving lists.

3.1.7.1. Congress Exercise


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 an initial 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 out just the one of the following three statements that is accurate:

1. You are eligible for both the House and Senate.


2. You eligible only for the House.
3. You are ineligible for Congress.

3.1.8. More String Methods


Here are a few more string methods useful in the next exercises:

s .startswith( pre )

returns True if
string s starts with string pre: Both
'-123'.startswith('-') and 'downstairs'.startswith('down') are
True , but '1 - 2 - 3'.startswith('-') is False .

s .endswith( suffix )

returns True if string s ends with string suffix: Both


'whoever'.endswith('ever') and 'downstairs'.endswith('airs') are
True , but '1 - 2 - 3'.endswith('-') is False .

s .replace( sub , replacement , count )

returns a new string with up to the first count occurrences of string


sub replaced by replacement. The replacement can be the empty
string to delete sub. For example:

s = '-123'
t = s.replace('-', '', 1) # t equals '123'
t = t.replace('-', '', 1) # t is still equal to '123'
u = '.2.3.4.'
v = u.replace('.', '', 2) # v equals '23.4.'
3.1.8.1. Article Start Exercise
In library alphabetizing, if the initial word is an article (“The”, “A”, “An”),
then it is ignored when ordering entries. Write a program completing this
function, and then testing it:

def startsWithArticle(title):
'''Return True if the first word of title is "The", "A" or
"An".'''

Be careful, if the title starts with “There”, it does not start with an article.
What should you be testing for?

3.1.8.2. Is Number String Exercise


** In the later Safe Number Input Exercise, it will be important to know if
a string can be converted to the desired type of number. Explore that
here. Save example isNumberStringStub.py as isNumberString.py and
complete it. It contains headings and documentation strings for the
functions in both parts of this exercise.

A legal whole number string consists entirely of digits. Luckily strings


have an isdigit method, which is true when a nonempty string consists
entirely of digits, so '2397'.isdigit() returns True , and '23a'.isdigit()
returns False , exactly corresponding to the situations when the string
represents a whole number!

In both parts be sure to test carefully. Not only confirm that all
appropriate strings return True . Also be sure to test that you return False
for all sorts of bad strings.

a. Recognizing an integer string is more involved, since it can start


with a minus sign (or not). Hence the isdigit method is not
enough by itself. This part is the most straightforward if you have
worked on the sections String Indices and String Slices. An
alternate approach works if you use the count method from Object
Orientation, and some methods from this section.

Complete the function isIntStr .

b. Complete the function isDecimalStr , which introduces the possibility


of a decimal point. The string methods mentioned in the previous
part remain useful.

[1] This is an improvement that is new in Python 3.0


[2] If you divide an even number by 2, what is the remainder? Use this
idea in your if condition.
[3] 4 tests to distinguish the 5 cases, as in the previous version
[4] Once again, you are calculating and returning a Boolean result.
You do not need an if - else statement.
Hands-on Python Tutorial » 3. More On Flow of Control » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Jan 18, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 3. More On Flow of Control » previous | next | index

Table Of Contents 3.2. Loops and Tuples


3.2. Loops and Tuples
3.2.1. Exercises This section will discuss several improvements to the chooseButton1.py
3.2.1.1. Choose program from the last section that will turn it into example program
Button Exercise
chooseButton2.py .
Previous topic
3.1. If Statements First an introduction to tuples, which we will use for the first time in this
section:
Next topic
3.3. While Statements A tuple is similar to a list except that a literal tuple is enclosed in
parentheses rather than square brackets, and a tuple is immutable. In
This Page particular you cannot change the length or substitute elements, unlike a
Show Source list. Examples are

Quick search (1, 2, 3)


('yes', 'no')

Go
They are another way to make several items into a single object. You
Enter search terms or a module,
can refer to individual parts with indexing, like with lists, but a more
class or function name. common way is with multiple assignment. A silly simple example:

tup = (1, 2)
(x, y) = tup
print(x) # prints 1
print(y) # prints 2

Now back to improving the chooseButton1.py program, which has similar


code repeating in several places. Imagine how much worse it would be if
there were more colors to choose from and more parts to color!

First consider the most egregious example:

if isInside(pt, redButton):
color = 'red'
elif isInside(pt, yellowButton):
color = 'yellow'
elif isInside(pt, blueButton):
color = 'blue'
else :
color = 'white'

Not only is this exact if statement repeated several times, all the
conditions within the if statement are very similar! Part of the reason I
did not put this all in a function was the large number of separate
variables. On further inspection, the particular variables redButton ,
yellowButton , blueButton , all play a similar role, and their names are not
really important, it is their associations that are important: that redButton
goes with ‘red’, .... When there is a sequence of things all treated
similarly, it suggests a list and a loop. An issue here is that the changing
data is paired, a rectangle with a color string. There are a number of
ways to handle such associations. A very neat way in Python to package
a pair (or more things together) is a tuple, turning several things into one
object, as in (redButtton, ‘red’). Objects such are this tuple can be put in
a larger list:

choicePairs = [(redButtton, 'red'), (yellowButton, 'yellow'),


(blueButton, 'blue')]

Such tuples may be neatly handled in a for statement. You can imagine
a function to encapsulate the color choice starting:

def getChoice(choicePairs, default, win):


'''Given a list choicePairs of tuples, with each tuple in the
form
(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. [1]

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. However 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

choicePairs = [(redButtton, 'red'), (yellowButton, 'yellow'),


(blueButton, 'blue')]

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(win.getWidth()/2, 375),'Click to choose a house


color.')
msg.draw(win)
color = getChoice(colorPairs, 'white', win)
house.setFill(color)

msg.setText('Click to choose a door color.')


color = getChoice(colorPairs, 'white', win)
door.setFill(color)

In the original version with the long if - elif statements, the interactive
portion only included portions for the user to set the color of two shapes
in the picture (or you would have been reading code forever). Looking
now at the similarity of the code for the two parts, we can imagine
another loop, that would easily allow for many more parts to be colored
interactively.

There are still several differences to resolve. First the message msg is
created the first time, and only the text is set the next time. That is easy
to make consistent by splitting the first part into an initialization and a
separate call to setText like in the second part:

msg = Text(Point(win.getWidth()/2, 375),'')


msg.draw(win)

msg.setText('Click to choose a house color.')

Then look to see the differences between the code for the two choices.
The shape object to be colored and the name used to describe the shape
change: two changes in each part. Again tuples can store the changes
of the form (shape, description). This is another place appropriate for a
loop driven by tuples.. The (shape, description) tuples should be
explicitly written into a list that can be called shapePairs . We could easily
extend the list shapePairs to allow more graphics objects to be colored.
In the code below, the roof is added.

The new interactive code can start with:

shapePairs = [(house, 'house'), (door, 'door'), (roof, 'roof')]


msg = Text(Point(win.getWidth()/2, 375),'')
msg.draw(win)
for (shape, description) in shapePairs:
prompt = 'Click to choose a ' + description + ' 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 --


Demonstate loops using lists of tuples of data.'''

from graphics import *

def isBetween(x, end1, end2):


'''Return True if x is between the ends or equal to either.
The ends do not need to be in increasing order.'''

return end1 <= x <= end2 or end2 <= x <= end1

def isInside(point, rect):


'''Return True if the point is inside the Rectangle rect.'''

pt1 = rect.getP1()
pt2 = rect.getP2()
return isBetween(point.getX(), pt1.getX(), pt2.getX()) and \
isBetween(point.getY(), pt1.getY(), pt2.getY())

def makeColoredRect(corner, width, height, color, win):


''' Return a Rectangle drawn in win with the upper left
corner
and color specified.'''

corner2 = corner.clone()
corner2.move(width, -height)
rect = Rectangle(corner, corner2)
rect.setFill(color)
rect.draw(win)
return rect

def getChoice(choicePairs, default, win): #NEW


'''Given a list choicePairs of tuples with each tuple in the
form
(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

def main():
win = GraphWin('pick Colors', 400, 400)
win.yUp()

#NEW
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))

house = makeColoredRect(Point(60, 200), 180, 150, 'gray',


win)
door = makeColoredRect(Point(90, 150), 40, 100, 'white', win)
roof = Polygon(Point(50, 200), Point(250, 200), Point(150,
300))
roof.setFill('black')
roof.draw(win)

#NEW
shapePairs = [(house, 'house'), (door, 'door'), (roof,
'roof')]
msg = Text(Point(win.getWidth()/2, 375),'')
msg.draw(win)
for (shape, description) in shapePairs:
prompt = 'Click to choose a ' + description + ' color.'
msg.setText(prompt)
color = getChoice(choicePairs, 'white', win)
shape.setFill(color)

win.promptClose(msg)

main()

Run it.

With the limited number of choices in chooseButton1.py , the change in


length to convert to chooseButton2.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!

3.2.1. Exercises
3.2.1.1. Choose Button Exercise
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 coordinate, 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.

[1] Particularly 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 this tutorial.
Hands-on Python Tutorial » 3. More On Flow of Control » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 3. More On Flow of Control » previous | next | index

Table Of Contents 3.3. While Statements


3.3. While Statements
3.3.1. Simple while
Loops
3.3.2. The Most General 3.3.1. Simple while Loops
range Function
3.3.3. Interactive while Other than the trick with using a return statement inside of a for loop, all
Loops of the loops so far have gone all the way through a specified list. In any
3.3.3.1. Interactive case the for loop has required the use of a specific list. This is often too
Sum Exercise
3.3.3.2. Safe restrictive. A Python while loop behaves quite similarly to common
Number Input English usage. If I say
Exercise
3.3.3.3. Savings While your tea is too hot, add a chip of ice.
Exercise
3.3.3.4. Strange Presumably you would test your tea. If it were too hot, you would add a
Sequence little ice. If you test again and it is still too hot, you would add ice again.
Exercise
3.3.4. Graphical
As long as you tested and found it was true that your tea was too hot,
Applications you would go back and add more ice. Python has a similar syntax:
3.3.4.1. Exercise
Moving Undraw while condition :
3.3.4.2. Make Path indentedBlock
Exercise
3.3.4.3. Random Setting up the English example in a similar format would be:
Start Exercise
3.3.4.4. Mad Lib while your tea is too hot :
While Exercise
3.3.4.5. Find Hole add a chip of ice
Game Exercise
3.3.5. Fancier Animation To make things concrete and numerical, suppose the following: The tea
Loop Logic (Optional) 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
Previous topic the temperature each time, and also print out the temperature before
3.2. Loops and Tuples reducing the temperature. In Python you could write and run the code
Next topic below, saved in example program cool.py:
3.4. Arbitrary Types Treated
As Boolean
1 temperature = 115
This Page 2
3
while temperature > 112: # first while loop code
print(temperature)
Show Source 4 temperature = temperature - 1
5
Quick search 6 print('The tea is cool enough.')

Go
I added a final line after the while loop to remind you that execution
follows sequentially after a loop completes.
Enter search terms or a module,
class or function name. If you play computer and follow the path of execution, you could generate
the following table. Remember, that each time you reach the end of the
indented block after the while heading, execution returns to the while
heading for another test:

Line temperature Comment


1 115
2 115 > 112 is true, do loop
3 prints 115
4 114 115 - 1 is 114, loop back
2 114 > 112 is true, do loop
3 prints 114
4 113 114 - 1 is 113, loop back
2 113 > 112 is true, do loop
3 prints 113
4 112 113 - 1 is 112, loop back
2 112 > 112 is false, skip loop
6 prints that the tea is cool

Each time the end of the indented loop body is reached, execution
returns to the while loop heading for another test. When the test is finally
false, execution jumps past the indented body of the while loop to the
next sequential statement.

A while loop generally follows the pattern of the successive modification


loop introduced with for -each loops:

initialization
while continuationCondition :
do main action to be repeated
prepare variables for the next time through the loop

Test yourself: Following the code. Figure out what is printed. :

i = 4
while i < 9:
print(i)
i = i+2

Check yourself by running the example program testWhile.py .

Note: In Python, while is not used quite like in English. In English you
could mean to stop as soon as the condition you want to test becomes
false. In Python the test is only made when execution for the loop
starts, 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.
1 i = 4 # variation on testWhile.py
2 while (i < 9):
3 i = i+2
4 print(i)

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 No test here
4 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 Most General range Function


There is actually a much simpler way to generate the previous
sequences like in testWhile3.py , 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 before printing:

nums = range(4, 9, 2)
print(list(nums))
The third parameter for the range function is the step size. It 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?

Try it: 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.

These ranges, like the simpler ranges that we used earlier, are most
often used as the sequence in a for loop heading:

for i in range(10, 0, -1): # countdown...


print(i)
print('Blastoff!')

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, in readLines0.py , the user is
prompted for the exact number of lines to be entered:

lines = list()
n = int(input('How many lines do you want to enter? '))
for i in range(n):
line = input('Next line: ')
lines.append(line)

print('Your lines were:') # check now


for line in lines:
print(line)
The user may want to enter a bunch of lines and not count them all
ahead of time. This means the number of repetitions would not be
known ahead of time. A while loop is appropriate here. There is still the
question of how to test whether the user wants to continue. An obvious
but verbose way to do this is to ask before every line if the user wants to
continue, as shown below and in the example file readLines1.py . Read it
and then run it:

lines = list()
testAnswer = input('Press y if you want to enter more lines: ')
while testAnswer == 'y':
line = input('Next line: ')
lines.append(line)
testAnswer = input('Press y if you want to enter more lines:
')

print('Your lines were:')


for line in lines:
print(line)

See the two statements setting testAnswer : one before the while loop and
one at the bottom of the loop body.

Note: The data must be initialized before the loop, in order for the
first test of the while condition to work. Also the test must work when
you loop back from the end of the loop body. This means the data for
the test must also be set up a second time, in the loop body. It is easy
to forget the second time!

The readLines1.py code works, but it may be more annoying than


counting ahead! Two lines must be entered for every one you actually
want! A practical alternative is to use a sentinel: a piece of data that
would not make sense in the regular sequence, and which is used to
indicate the end of the input. You could agree to use the line DONE! Even
simpler: if you assume all the real lines of data will actually have some
text on them, use an empty line as a sentinel. (If you think about it, the
Python Shell uses this approach when you enter a statement with an
indented body.) This way you only need to enter one extra (very simple)
line, no matter how many lines of real data you have.

What should the while condition be now? Since the sentinel is an empty
line, you might think line == '' , but that is the termination condition, not
the continuation condition: You need the opposite condition. To negate a
condition in Python, you may use not , like in English,

not line == ''

Of course in this situation there is a shorter way,


line != ''

Run the example program readLines2.py , shown below:

lines = list()
print('Enter lines of text.')
print('Enter an empty line to quit.')

line = input('Next line: ') # initalize before the loop


while line != '': # while NOT the termination condition
lines.append(line)
line = input('Next line: ') # !! reset value at end of loop!

print('Your lines were:')


for line in lines:
print(line)

Again the data for the test in the while loop heading must be initialized
before the first time the while statement is executed and the test data
must also be made ready inside the loop for the test after the body has
executed. Hence you see the statements setting the variable line both
before the loop and at the end of the loop body. It is easy to forget the
second place inside the loop!

After reading the rest of this paragraph, comment the last line of the loop
out, and run it again: It will never stop! The variable line will forever
have the initial value you gave it! You actually can stop the program by
entering Ctrl-C . That means hold the Ctrl key and press c .

Note: As you finish coding a while loop, it is good practice to always


double-check: Did I make a change to the variables, inside the loop,
that will eventually make the loop condition False ?

The earliest while loop examples had numerical tests and the code to get
ready for the next loop just incremented a numerical variable by a fixed
amount. Those were simple examples but while loops are much more
general! In the interactive loop we have seen a continuation condition
with a string test, and getting ready for the next time through the loop
involves input from the user.

Some of the exercises that follow involve interactive while loops. Others
were delayed until here just because they have a wider variety of
continuation condition tests and ways to prepare for the next time
through the loop. What is consistent is the general steps to think of and
questions to ask yourself. They keep on applying! Keep these in mind!

the need to see whether there is a kind of repetition,


even without a fixed collection of values to work
through
to think from the specific situation and figure out the
continuation condition that makes sense for your loop
to think what specific processing or results you want
each time through the loop, using the same code
to figure out what supporting code you need to make
you ready for the next time through the loop: how to
make the same results code have new data values to
process each time through, and eventually reach a
stopping point.

3.3.3.1. Interactive Sum Exercise


Write a program sumAll.py that prompts the user to enter numbers, one
per line, ending with a line containing 0, and keep a running sum of the
numbers. Only print out the sum after all the numbers are entered (at
least in your final version). Do not create a list! Each time you read in a
number, you can immediately use it for your sum, and then be done with
the number just entered.

3.3.3.2. Safe Number Input Exercise


* There is an issue with reading in numbers with the input statement. If
you make a typo and enter something that cannot be converted from a
string to the right kind of number, a naive program will bomb. This is
avoidable if you test the string and repeat if the string is illegal. In this
exercise write safe utility function replacements for the input function
that work to read in a whole number, an integer or a decimal number.

All parts refer to the previous Is Number String Exercise. Part a. refers to
the introduction in the previous exercise. Parts b. and c. refer to
functions in the solution, isNumberStr.py , of the previous exercise. Make
sure you look back at these first.

Save the example safeNumberInputStub.py as safeNumberInput.py , and


complete it. It contains headings and documentation strings for the
functions in each part of this exercise.

a. This part considers the simplest case, where you are trying to enter
a whole number. Complete the definition of the function
safeWholeNumber .
b. Complete the function safeInt . This easily parallels part a. if you
copy in and use the function (not method) isIntegerStr .
c. Complete the function safeDecimal . This easily parallels part b. if
you copy in and use the function isDecimalStr .

3.3.3.3. Savings Exercise


The idea here is to see how many years it will take a bank account to
grow to at least a given value, assuming a fixed annual interest. Write a
program savings.py . Prompts the user for three numbers: an initial
balance, the annual percentage for interest as a decimal. like .04 for 4%,
and the final balance desired. Print the initial balance, and the balance
each year until the desired amount is reached. Round displayed
amounts to two decimal places, as usual.

The math: The amount next year is the amount now times (1 + interest
fraction), so if I have $500 now and the interest rate is .04, I have $500*
(1.04) = $520 after one year and after two years I have, $520*(1.04) =
$540.80. If I enter into the program a $500 starting balance, .04 interest
rate and a target of $550, the program prints:

500.00
520.00
540.80
562.43

3.3.3.4. Strange Sequence Exercise


* Recall Strange Function Exercise and its jumpFunc.py which contains
the function jump : For any integer n, jump(n) is n//2 if n is even, and
3*n+1 if n is odd.

You can start with one number, say n = 3, and keep applying the jump
function to the last number given, and see how the numbers jump
around!

jump(3) = 3*3+1 = 10; jump(10) = 10//2 = 5;


jump(5) = 3*5+1 = 16; jump(16) = 16//2 = 8;
jump(8) = 8//2 = 4; jump(4) = 4//2 = 2;
jump(2) = 2//2 = 1

This process of repeatedly applying the same function to the most recent
result is called function iteration. In this case you see that iterating the
jump function, starting from n=3, eventually reaches the value 1.

It is an open research question whether iterating the jump function from


an integer n will eventually reach 1, for every starting integer n greater
than 1. Researchers have only found examples of n where it is true. Still,
no general argument has been made to apply to the infinite number of
possible starting integers.

In this exercise you iterate the jump function for specific starting values n,
until the result is 1.

a. Save example jumpSeqStub.py as jumpSeq.py and complete the


missing function bodies. If you coded the function jump before in
jumpFunc.py , you can copy it. You can complete either printJumps
or listJumps first, and test before completing the other. Hint [1]

b. After you have finished and saved jumpSeq.py copy it and save the
file as jumpSeqLengths.py .

First modify the main method so it prompts the user for a value of
n, and then prints just the length of the iterative sequence from
listJumps(n). Hint [2]

Then elaborate the program so it prompts the user for two integers:
a lowest starting value of n and a highest starting value of n. For all
integers n in the range from the lowest start through the highest
start, including the highest, print a sentence giving the starting
value of n and the length of the list from listJumps(n) . An example
run:

Enter lowest start: 3


Enter highest start: 6
Starting from 3, jump sequence length 8.
Starting from 4, jump sequence length 3.
Starting from 5, jump sequence length 6.
Starting from 6, jump sequence length 9.

3.3.4. Graphical Applications


Another place where a while loop could be useful is in interactive
graphics. Suppose you want the user to be able to create a Polygon by
clicking on vertices they choose interactively, but you do not want them
to have to count the number of vertices ahead of time. A while loop is
suggested for such a repetitive process. As with entering lines of text
interactively, there is the question of how to indicate that you are done
(or how to indicate to continue). If you make only a certain region be
allowed for the Polygon, then the sentinel can be a mouse click outside
the region. The earlier interactive color choice example already has a
method to check if a mouse click is inside a Rectangle, so that method
can be copied and reused.

Creating a polygon is a unified activity with a clear result, so let’s define a


function. It involves a boundary rectangle and mouse clicks in a
GraphWin, and may as well return the Polygon constructed. Read the
following start:

def polyHere(rect, win):


''' Draw a polygon interactively in Rectangle rect, in
GraphWin win.
Collect mouse clicks inside rect into the vertices of a
Polygon,
and always draw the Polygon created so far.
When a click goes outside rect, stop and return the final
polygon.
The Polygon ends up drawn. The method draws and undraws
rect.
'''

It is useful to start by thinking of the objects needed, and give them


names.

A Polygon is needed. Call it poly .


A list of vertices is needed. Call it vertices . I need to append to this
list. It must be initialized first.
The latest mouse click point is needed. Call it pt .

Certainly the overall process will be repetitious, choosing point after


point. Still it may not be at all clear how to make an effective Python
loop. In challenging situations like this it is often useful to imagine a
concrete situation with a limited number of steps, so each step can be
written in sequence without worrying about a loop.

For instance to get up to a triangle (3 vertices in our list and a fourth


mouse click for the sentinel), you might imagine the following sequence,
undrawing each old polygon before the next is displayed with the latest
mouse click included:

rect.setOutline('red')
rect.draw(win)
vertices = list()
pt = win.getMouse()
vertices.append(pt)
poly = Polygon(vertices)
poly.draw(win) # with one point
pt = win.getMouse()
poly.undraw() # missing latest point
vertices.append(pt)
poly = Polygon(vertices)
poly.draw(win) # with two points
pt = win.getMouse()
poly.undraw() # missing latest point
vertices.append(pt)
poly = Polygon(vertices)
poly.draw(win) # with three points
pt = win.getMouse() # assume outside the region

rect.undraw()
return poly

There is a fine point here that I missed the first time. The vertices of an
existing Polygon do not get mutated in this system. A new Polygon gets
created each time with the new vertex list. The old Polygon does not go
away automatically, and extraneous lines appear in the picture if the old
polygon is not explicitly undrawn each time before a new version is
redrawn with an extra vertex. The last Polygon you draw should be
visible at the end, so in the example above where I was assuming the
third click was the last for the triangle, I did not undraw the Polygon.
The timing for each undraw needs to be after the next mouse click and
presumably before the next Polygon is created, so it could be before or
after the line vertices.append(pt) . I arbitrarily chose for it to go before the
vertices list is changed. The rest of the order of the lines is pretty well
fixed by the basic logic.

If you think of the repetitions through a large number of loops, the


process is essentially circular (as suggested by the word ‘loop’). The
body of a loop in Python, however, is written as a linear sequence: one
with a first line and a last line, a beginning and an end. We can cut a
circular loop anywhere to get a piece with a beginning and an end. In
practice, the place you cut the loop for Python has one main constraint:
The processing in Python from the end of one time through the loop to
the beginning of the next loop is separated by the test of the condition in
the heading. The continuation condition in the while heading must make
sense where you cut the loop.

It can help to look at a concrete example sequence, like the steps listed
above for creating a triangle, only now assuming we do not know how
many vertices will be chosen. The continuation condition is for pt to be
in the rectangle, so using the previously written function isInside , the
loop heading will be

while isInside(pt, rect):

With this condition in mind, look for where to split to loop. It needs to be
after a new pt is clicked (so it can be tested) and before the next
Polygon is created (so it does not include the sentinel point by mistake).
In particular, with the sequence above, look and see that the split could
go before or after the poly.undraw() line. Exercise Moving Undraw
considers the case where the split goes before this line. I will proceed
with the choice of splitting into a Python loop after the undraw line. This
makes the loop be

while isInside(pt, rect):


vertices.append(pt)
poly = Polygon(vertices)
poly.draw(win)
pt = win.getMouse()
poly.undraw()

If you follow the total sequence of required steps above for making the
concrete triangle, you see that this full sequence for the loop is only
repeated twice. The last time there is no poly.undraw() step. I could redo
the loop moving the undraw line to the top, which caused different
issues (Exercise Moving Undraw below). Instead think how to make it
work at the end of the final time through the loop....

There are several possible approaches. You want the undraw line every
time except for the last time. Hence it is a statement you want
sometimes and not others. That suggests an if statement. The times
you want the undraw are when the loop will repeat again. This is the
same as the continuation condition for the loop, and you have just read
the next value for pt ! You could just add a condition in front of the last
line of the loop:

if isInside(pt, rect):
poly.undraw()

I find this option unaesthetic: it means duplicating the continuation test


twice in every loop.

Instead of avoiding the undraw as you exit the loop, another option in this
case is to undo it: just redraw the polygon one final time beyond the
loop. This only needs to be done once, not repeatedly in the loop. Then
the repetitious lines collapse neatly into the loop. If you look at the
overall concrete sequence for the triangle, not all the lines are in the
loop. You must carefully include the lines both that come before the loop
and those that come after the loop. Make sure these lines are not put in
the loop, but before or after, as indicated by the concrete sequence in
the example. In the end the entire function is:

def polyHere(rect, win):


''' Draw a polygon interactively in Rectangle rect, in
GraphWin win.
Collect mouse clicks inside rect into the vertices of a
Polygon,
and always draw the Polygon created so far.
When a click goes outside rect, stop and return the final
polygon.
The Polygon ends up drawn. The method draws and undraws
rect.
'''
rect.setOutline("red")
rect.draw(win)
vertices = list()
pt = win.getMouse()
while isInside(pt, rect):
vertices.append(pt)
poly = Polygon(vertices)
poly.draw(win)
pt = win.getMouse()
poly.undraw()
poly.draw(win)
rect.undraw()
return poly

Make sure you understand: Follow this code through, imagining three
mouse clicks inside rect and then one click outside of rect. Compare the
steps to the ones in the concrete sequence written out above and see
that the match (aside from the last canceling undraw and draw of poly ).

This function is illustrated in the example program makePoly.py . Other


than standard graphics example code, the main program contains:

rect1 = Rectangle(Point(5, 55), Point(200, 120))


poly1 = polyHere(rect1, win)
poly1.setFill('green')
rect2 = Rectangle(Point(210, 50), Point(350, 350))
poly2 = polyHere(rect2, win)
poly2.setOutline('orange')

As you can see, the returned polygons are used to make color changes,
just as an illustration.

In earlier animation examples a while loop would also have been useful.
Rather than continuing the animation a fixed number of times, it would
be nice for the user to indicate by a mouse click when she has watched
long enough. Thus far the only way to use the mouse has been with
getMouse() . This is not going to work in an animation, because the
computer stops and waits for a click with getMouse() , whereas the
animation should continue until the click.

In full-fledged graphical systems that respond to events, this is no


problem. Zelle’s graphics is built on top of a capable event-driven
system, and in fact, all mouse clicks are registered, even outside calls to
getMouse() .

As an example, run example program randomCirclesWhile.py . Be sure to


follow the prompt saying to click to start and to end.

Aside from the prompts, the difference from the previous


randomCircles.py program is the replacement of the original simple
repeat loop heading

for i in range(75):

by the following initialization and while loop heading:

while win.checkMouse() == None: #NEW*

The graphics module remembers the last mouse click, whether or not it
occurred during a call to getMouse() . A way to check if the mouse has
been clicked since the last call to getMouse() is checkMouse() . It does not
wait for the mouse as in getMouse() . Instead it returns the remembered
mouse click - the most recent mouse click in the past, unless there has
been no mouse click since the last call to getMouse or checkMouse. In
that case checkMouse() returns None (the special object used to indicate
the lack of a regular object).

The checkMouse method allows for a loop that does not stop while waiting
for a mouse click, but goes on until the heading test detects that the
mouse was clicked.

A similar elaboration can be made for the other examples of animation,


like bounce1.py . In bounceWhile.py I modified bounce1.py to have a while
loop in place of the for-loop repeating 600 times. Run it. The only slight
added modification here was that win was not originally a parameter to
bounceInBox , so I included it. Look at the source code for bounceWhile.py ,
with the few changes marked NEW.

In bounce2.py I also made a more interesting change to the initialization,


so the initial direction and speed of the mouse are determined
graphically by the user, with a mouse click. Try example program
bounce2.py .

The program includes a new utility function to help determine the initial
(dx, dy) for the animation. This is done by calculating the move
necessary to go from one point (where the ball is in this program) to
another (specified by a user’s mouse click in this program). :

def getShift(point1, point2): # NEW utility function


'''Returns a tuple (dx, dy) which is the shift from point1 to
point2.'''
dx = point2.getX() - point1.getX()
dy = point2.getY() - point1.getY()
return (dx, dy)

Since the function calculates both a change in x and y, it returns a tuple .

A straightforward interactive method, getUserShift , is wrapped around


this function to get the user’s choice, which ultimately returns the same
tuple:

def getUserShift(point, prompt, win): #NEW direction selection


« '''Return the change in position from the point to a mouse
click in win.
First display the prompt string under point.'''

text = Text(Point(point.getX(), 60), prompt)


text.draw(win)
userPt = win.getMouse()
text.undraw()
return getShift(point, userPt)

In the new version of the main driver, bounceBall , excerpted below, this
interactive setting of (dx, dy) is used. Note the multiple assignment
statement to both dx and dy, set from the tuple returned from
getUserShift . This shift would generally be much too much for a single
animation step, so the actual values passed to bounceBall are scaled
way down by a factor scale .

center = Point(win.getWidth()/2, win.getHeight()/2) #NEW


central starting point
ball = makeDisk(center, radius, win)

#NEW interactive direction and speed setting


prompt = '''
Click to indicate the direction and
speed of the ball: The further you
click from the ball, the faster it starts.'''
(dx, dy) = getUserShift(center, prompt, win)
scale = 0.01 # to reduce the size of animation steps
bounceInBox(ball, dx*scale, dy*scale, xLow, xHigh, yLow,
yHigh, win)

The bounceInBox method has the same change to the loop as in the
randomCircles.py example. The method then requires the GraphWin , win ,
as a further parameter, since checkMouse is a GraphWin method.

You can look in Idle at the full source code for bounce2.py if you like. The
changes from bounce1.py are all marked with a comment starting with
#NEW , and all the major changes have been described above.

In the examples so far of the use of checkMouse() , we have only used the
fact that a point was clicked, not which point. The next example version,
bounce3.py , does use the location of mouse clicks that are read with
checkMouse() to change the direction and speed of the ball. Try it.

This version only slightly modifies the central animation function,


bounceInBox , but wraps it in another looping function that makes the
direction and speed of the ball change on each mouse click. Hence the
mouse clicks detected in bounceInBox need to be remembered and then
returned after the main animation loop finishes. That requires a name,
pt , to be given to the last mouse click, so it can be remembered. This
means modifying the main animation loop to initialize the variable pt
before the loop and reset it at the end of the loop, much as in the use of
getMouse() for the interactive polygon creation. That explains the first
three NEW lines and the last two NEW lines in the revised bounceInBox :

def bounceInBox(shape, dx, dy, xLow, xHigh, yLow, yHigh, win):


''' Animate a shape moving in jumps (dx, dy), bouncing when
its center reaches the low and high x and y coordinates.
The animation stops when the mouse is clicked, and the
last mouse click is returned.'''

delay = .001
pt = None #NEW
while pt == None: #NEW
shape.move(dx, dy)
center = shape.getCenter()
x = center.getX()
y = center.getY()
isInside = True #NEW
if x < xLow or x > xHigh:
dx = -dx
isInside = False #NEW
if y < yLow or y > yHigh:
dy = -dy
isInside = False #NEW
time.sleep(delay)
if isInside: # NEW don't mess with dx, dy when outside
pt = win.checkMouse() #NEW
return pt #NEW

def moveInBox(shape, stopHeight, xLow, xHigh, yLow, yHigh, win):


#NEW
'''Shape bounces in win so its center stays within the low
and high
x and y coordinates, and changes direction based on mouse
clicks,
terminating when there is a click above stopHeight.'''

scale = 0.01
pt = shape.getCenter() # starts motionless
while pt.getY() < stopHeight:
(dx, dy) = getShift(shape.getCenter(), pt)
pt = bounceInBox(shape, dx*scale, dy*scale,
xLow, xHigh, yLow, yHigh, win)

def makeDisk(center, radius, win):


'''Return a red disk that is drawn in win with given center
and radius.'''
disk = Circle(center, radius)
disk.setOutline("red")
disk.setFill("red")
disk.draw(win)
return disk

def getShift(point1, point2):


'''Returns a tuple (dx, dy) which is the shift from point1 to
point2.'''
dx = point2.getX() - point1.getX()
dy = point2.getY() - point1.getY()
return (dx, dy)

def bounceBall():
'''Make a ball bounce around the screen, and react to mouse
clicks.'''

win = GraphWin('Ball Bounce 3', 290, 290)


win.yUp()

#NEW to mark and label the area where a click stops the
program
lineHeight = win.getHeight() - 40
textHeight = win.getHeight() - 20
Line(Point(0, lineHeight), Point(win.getWidth(),
lineHeight)).draw(win)
prompt = 'Click above the line to stop\nor below to move
toward the click.'
Text(Point(win.getWidth()/2, textHeight), prompt).draw(win)

radius = 10
xLow = radius # center is separated from the wall by the
radius at a bounce
xHigh = win.getWidth() - radius
yLow = radius
yHigh = lineHeight - radius #NEW lower top to bouncing
limits

center = Point(win.getWidth()/2, lineHeight/2)


ball = makeDisk(center, radius, win)
moveInBox(ball, lineHeight, xLow, xHigh, yLow, yHigh, win)
#NEW

win.close()

bounceBall()

I initially made only the changes discussed so far (not the ones involving
the new variable isInside ). The variable isInside was in response to a
bug that I will discuss after introducing the simple function that wraps
around bounceInBox :

Each time the mouse is clicked, the ball is to switch direction and move
toward the last click, until the stopping condition occurs, when there is a
click above the stop line. This is clearly repetitive and needs a while
loop. The condition is simply to test the y coordinate of the mouse click
against the the height of the stop line. The body of the loop is very short,
since we already have the utility function getShift , to figure out (dx, dy)
values.

def moveInBox(shape, stopHeight, xLow, xHigh, yLow, yHigh, win):


#NEW
'''Shape bounces in win so its center stays within the low
and high
x and y coordinates, and changes direction based on mouse
clicks,
terminating when there is a click above stopHeight.'''

scale = 0.01
pt = shape.getCenter() # starts motionless
while pt.getY() < stopHeight:
(dx, dy) = getShift(shape.getCenter(), pt)
pt = bounceInBox(shape, dx*scale, dy*scale,
xLow, xHigh, yLow, yHigh, win)

The variable pt for the last mouse click needed to be initialized some
way. I chose to make the value be the same as the initial position of the
ball, so both dx and dy are initially 0, and the ball does not start in
motion. (Alternatives are in Random Start Exercise below.)

I occasionally detected a bug when using the program. The ball would
get stuck just outside the boundary and stay there. The fact that it was
slightly beyond the boundary was a clue: For simplicity I had cheated,
and allowed the ball to go just one animation step beyond the intended
boundary. With the speed and small step size this works visually. The
original code was sure to make an opposite jump back inside at the next
step.

After some thought, I noticed that the initial version of the bounce3.py
code for bounceInBox broke that assumption. When the ball was where a
bounce-back is required, a mouse click could change (dx, dy) and mess
up the bounce. The idea for a fix is not to let the user change the
direction in the moment when the ball needs to bounce back.

Neither of the original boundary-checking if statements, by itself, always


determines if the ball is in the region where it needs to reverse direction.
I dealt with this situation by introducing a Boolean variable isInside . It is
initially set as True , and then either of the if statements can correct it to
False. Then, at the end of the loop, isInside is used to make sure the
ball is safely inside the proper region when there is a check for a new
mouse click and a possible user adjustment to (dx, dy).

3.3.4.1. Exercise Moving Undraw


** As discussed above at Where to split the loop, the basic loop logic
works whether the poly.undraw() call is at the beginning or end of the
loop. Write a variation makePoly2.py that makes the code work the other
way, with the poly.undraw() at the beginning of the loop. The new place
to cut the loop does affect the code before and after the loop. In
particular, the extra statement drawing poly is not needed after the loop
is completed. Make other changes to the surrounding code to make this
work. Hints: [3]

3.3.4.2. Make Path Exercise


** Write a program very similar to makePoly.py , and call it makePath.py ,
with a function pathHere . The only outward difference between polyHere
and pathHere is that while the first creates a closed polygon, and returns
it, and the new one creates a polygonal path, without the final point
being automatically connected to the first point, and a list of the lines in
the path is returned. Internally the functions are quite different. The
change simplifies some things: no need to undraw anything in the main
loop - just draw the latest segment each time going from the previous
point to the just clicked point. There is a complication however, you do
need deal specially with the first point. It has no previous point to
connect to. I suggest you handle this before the main loop, and draw the
point so it is a visible guide for the next point. After your main loop is
finished undraw this initial point. (The place on the screen will still be
visible if an initial segment is drawn. If no more points were added, the
screen is left blank, which is the way it should be.) You also need to
remember the previous point each time through the main loop.

In your main program, test the makePath function several times. Use the
list of lines returned to loop and change the color in one path and the
width of the lines in another path. A portion of a sample image is shown
below after all this is done.
3.3.4.3. Random Start Exercise
* (Optional) I chose to have the ball start motionless, by making the initial
value of pt (which determines the initial (dx, dy) ) be the center of the
ball. Write a variation startRandom.py so pt is randomly chosen. Also
make the initial location of the ball be random. You can copy the function
getRandomPoint from bounce1.py.

3.3.4.4. Mad Lib While Exercise


** Write a program madlib4.py that modifies the getKeys method of
madlib2.py to use a while loop. (This is not an animation program, but
this section is where you have had the most experience with while
loops!)

Hints: This is actually the most natural approach. I avoided while loops
initially, when only for loops had been discussed. It is redundant in the
original approach, however, to find every instance of '{' to count the
number of repetitions and then find them all again when extracting the
cue keys. A more natural way to control the loop is a while loop stopping
when there are no further occurrences of '{' to find . This involves
some further adjustments. You must cut the loop in a different place (to
end after searching for '{' ). As discussed before, cutting a loop in a
different place may require changes before and after the loop, too.

3.3.4.5. Find Hole Game Exercise


** Write a graphical game program, findHole.py , “Find the Hole”. The
program should use a random number generator to determine a circular
“hole”, selecting a point and a perhaps the radius around that point.
These determine the target and are not revealed to the player initially.
The user is then prompted to click around on the screen to “find the
hidden hole”. You should show the points the user has tried. Once the
user selects a point that is within the chosen radius of the mystery point,
the mystery circle should appear. There should be a message
announcing how many steps it took, and the game should end.

Hint: you have already seen the code to determine the displacement (dx,
dy) between two points: use the getShift function in bounce2.py . Once
you have the displacement (dx, dy) between the hidden center and the
latest mouse click, the distance between the points is (dx*dx +
dy*dy)**0.5 , using the Pythagorean Theorem of geometry. If this
distance is no more than the radius you have chosen for the mystery
circle, then the user has found the circle! You can use getShift as
written, or modify it into a function getDistance that directly returns the
distance between two points.

Many elaborations on this game are possible! Have fun with it!

3.3.5. Fancier Animation Loop Logic


(Optional)
The final variation is the example program bounce4.py , which has the
same outward behavior as bounce3.py, but it illustrates a different
internal design decision. The bounce3.py version has two levels of while
loop in two methods, moveInBox for mouse clicks and bounceInBox for
bouncing. The bounce4.py version puts all the code for changing
direction inside the main animation loop in the old outer function,
moveInBox . There are now three reasons to adjust (dx, dy): bouncing off
the sides, bouncing off the top or bottom, or a mouse click. That is a
simplification and unification of the logic in one sense. The complication
now is that the logic for determining when to quit is buried deep inside
the if - else logic, not at the heading of the loop. The test for mouse
clicks is inside the while loop and further inside another if statement.
The test of the mouse click may merely lead to a change in (dx, dy), or is
a signal to quit. Here is the revised code, with a discussion afterward of
the return statement:

def moveInBox(shape, stopHeight, xLow, xHigh, yLow, yHigh, win):


''' Animate a shape moving toward any mouse click below
stopHeight and
bouncing when its center reaches the low or high x or y
coordinates.
The animation stops when the mouse is clicked at stopHeight
or above.'''

scale = 0.01
delay = .001
dx = 0 #NEW dx and dy are no longer
parameters
dy = 0 #NEW
while True: #NEW exit loop at return
statement
center = shape.getCenter()
x = center.getX()
y = center.getY()
isInside = True
if x < xLow or x > xHigh:
dx = -dx
isInside = False
if y < yLow or y > yHigh:
dy = -dy
isInside = False
if isInside:
pt = win.checkMouse()
if pt != None: #NEW dealing with mouse
click now here
if pt.getY() < stopHeight: # switch direction
(dx, dy) = getShift(center, pt)
(dx, dy) = (dx*scale, dy*scale)
else: #NEW exit from depths of
the loop
return #NEW
shape.move(dx, dy)
time.sleep(delay)

Recall that a return statement immediately terminates function


execution. In this case the function returns no value, but a bare return is
legal to force the exit. Since the testing is not done in the normal while
condition, the while condition is set as permanently True . This is not the
most common while loop pattern! It obscures the loop exit. The choice
between the approach of bounce3.py and bounce4.py is a matter of taste
in the given situation.

[1] You will need a loop. You can print/append almost all the numbers
in the loop. You are likely to omit one number with just this code,
but after looking at what you produce, it is easy to separately
include the remaining number. There are several ways to do this.
[2] Recall the built-in len function! It applies to lists.
[3] The basic issue is similar to the old version: the undraw is not
always needed, at the beginning in this case. In this place it is not
need the first time through the loop. The two basic approaches
considered for the previous version still work here: break into
cases inside the loop or make an extra compensating action
outside the loop. Further hint: It is legal to draw a polygon with an
empty vertex list - nothing appears on the screen.
Hands-on Python Tutorial » 3. More On Flow of Control » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on May 24, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 3. More On Flow of Control » previous | next | index

Previous topic 3.4. Arbitrary Types Treated As


3.3. While Statements
Boolean
Next topic
3.5. Further Topics to The following section would merely be an advanced topic, except for the
Consider fact that many common mistakes have their meaning changed and
This Page obscured by the Boolean syntax discussed.

Show Source You have seen how many kinds of objects can be converted to other
types. Any object can be converted to Boolean (type bool). Read the
Quick search
examples shown in this Shell sequence:

Go
>>> bool(2)
True
>>> bool(-3.1)
Enter search terms or a module, True
class or function name. >>> bool(0)
False
>>> bool(0.0)
False
>>> bool(None)
False
>>> bool('')
False
>>> bool('0')
True
>>> bool('False')
True
>>> bool([])
False
>>> bool([0])
True

The result looks pretty strange, but there is a fairly short general
explanation: Almost everything is converted to True . The only values
among built-in types that are interpreted as False are

The Boolean value False itself


Any numerical value equal to 0 (0, 0.0 but not 2 or -3.1)
The special value None
Any empty sequence or collection, including the empty string( '' ,
but not '0' or 'hi' or 'False') and the empty list ( [] , but not [1,2,
3] or [0] )

A possibly useful consequence occurs in the fairly common situation


where something needs to be done with a list only if it is nonempty. In
this case the explicit syntax:

if len(aList) > 0:
doSomethingWith(aList)
can be written with the more succinct Pythonic idiom

if aList:
doSomethingWith(aList)

This automatic conversion can also lead to extra trouble! Suppose you
prompt the user for the answer to a yes/no question, and want to accept
‘y’ or ‘yes’ as indicating True. You might write the following incorrect
code. Read it:

ans = input('Is this OK? ')


if ans == 'y' or 'yes':
print('Yes, it is OK')

The problem is that there are two binary operations here: == , or .


Comparison operations all have higher precedence than the logical
operations or , and , and not . The if condition above can be rewritten
equivalently with parentheses. Read and consider:

(ans == 'y') or 'yes'

Other programming languages have the advantage of stopping with an


error at such an expression, since a string like 'yes' is not Boolean.
Python, however, accepts the expression, and treats 'yes' as True ! To
test, run the example program boolConfusion.py , shown below:

ans = 'y'
if ans == 'y' or 'yes':
print('y is OK')

ans = 'no'
if ans == 'y' or 'yes':
print('no is OK!!???')

Python detects no error. The or expression is always treated as True ,


since 'yes' is a non-empty sequence, interpreted as True .

The intention of the if condition presumably was something like

(ans == 'y') or (ans == 'yes')

This version also translates directly to other languages. Another correct


Pythonic alternative that groups the alternate values together is
«
ans in ['y', 'yes']

which reads pretty much like English. It is true if ans is in the specified
list. The in operator actually works with any sequence. The general
syntax is
value in sequence

This is true when value is an element of the sequence.

Be careful to use a correct expression when you want to specify a


condition like this.

Things get even stranger! Enter these conditions themselves, one at a


time, directly into the Shell:

'y' == 'y' or 'yes'


'no' == 'y' or 'yes'

The meaning of (a or b) is exactly as discussed so far if each of the


operands a and b are actually Boolean, but a more elaborate definition
is needed if an operand is not Boolean.

val = a or b

means

if bool(a):
val = a
else:
val = b

and in a similar vein:

val = a and b

means

if bool(a):
val = b
else:
val = a

This strange syntax was included in Python to allow code like in the
following example program orNotBoolean.py . Read and test if you like:

defaultColor = 'red'
userColor = input('Enter a color, or just press Enter for the
default: ')
color = userColor or defaultColor
print('The color is', color)

which sets color to the value of defaultColor if the user enters an empty
string.

Again, this may be useful to experienced programmers. The syntax can


certainly cause difficult bugs, particularly for beginners!
The not operator always produces a result of type bool.

Hands-on Python Tutorial » 3. More On Flow of Control » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 24, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 3. More On Flow of Control » previous | next | index

Previous topic 3.5. Further Topics to Consider


3.4. Arbitrary Types Treated
As Boolean Chapter 4 gives an application of Python to the web. It does not
introduce new language features. We have come to end of the new
Next topic
language features in this tutorial, but there are certainly more basic
3.6. Summary
topics to learn about programming and Python in particular, if you
This Page continue in other places:
Show Source 1. Creating your own kinds of objects (writing classes)
Quick search 2. Inheritance: Building new classes derived from existing classes
3. Python list indexing and slicing, both to read and change parts of
lists
Go 4. Other syntax used with loops: break , continue , and else
5. Exception handling
Enter search terms or a module,
class or function name.
6. Python’s variable length parameter lists, and other options in
parameter lists
7. List comprehensions: a concise, readable, fast Pythonic way to
make new lists from old ones
8. Event handling in graphical programming
9. Recursion (not special to Python): a powerful programming
technique where functions call themselves

Beyond these language features, Python has a vast collection of useful


modules. An example program, bbassign.py , is a real-world program that
«
I have in regular use for processing inconveniently organized files
created by Blackboard for homework submissions. It is a command-line
script that uses string methods and slicing and both kinds of loops, as
well is illustrating some useful components in modules sys, os, and
os.path, for accessing command line parameters, listing file directories,
creating directories, and moving and renaming files.

Hands-on Python Tutorial » 3. More On Flow of Control » previous | next | index


© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 24, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 3. More On Flow of Control » previous | next | index

Previous topic 3.6. Summary


3.5. Further Topics to
Consider 1. Comparison operators produce a Boolean result (type bool , either
Next topic True or False ): [More Conditional Expressions]

4. Dynamic Web Pages


Math
This Page Meaning Symbol Python Symbols
Show Source
Less than < <
Quick search
Greater than > >

Go Less than or equal ≤ <=

Enter search terms or a module,


Greater than or equal ≥ >=
class or function name.

Equals = ==

Not equal ≠ !=

Comparisons may be chained as in a < b <= c < d != e . [Multiple


Tests and if-elif Statements]

2. The in operator: [Arbitrary Types Treated As Boolean]

value in sequence

is True if value is one of the elements in the sequence.

3. Interpretation as Boolean ( True , False ): All Python data may be


converted to Boolean (type bool ). The only built-in data that have a
Boolean meaning of False , in addition to False itself, are None ,
numeric values equal to 0, and empty collections or sequences,
like the empty list {[]}and the empty string '' . [Arbitrary Types
Treated As Boolean]

4. Operators on Boolean expressions [Compound Boolean


Expressions]

1. condition1 and condition2


True only if both conditions are True

2. condition1 or condition2
True only if at least one condition is True

3. not condition
True only when condition is False

This description is sufficient if the result is used as a Boolean value


(in an if or while condition). See Arbitrary Types Treated As
Boolean for the advanced use when operands are not explicitly
Boolean, and the result is not going to be interpreted as Boolean.

5. if Statements

1. Simple if statement [Simple if Statements]

if condition :
indentedStatementBlockForTrueCondition

If the condition is True , then do the indented statement block.


If the condition is not True , then skip it.

2. if-else statement [if-else Statements]

if condition :
indentedStatementBlockForTrueCondition
else:
indentedStatementBlockForFalseCondition

If the condition is True, then do the first indented block only. If


the condition is not True , then skip the first indented block
and do the one after the else: .

3. The most general syntax for an if statement, if - elif - else


[Multiple Tests and if-elif Statements]

if condition1 :
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.

4. if - elif
[Multiple Tests and if-elif Statements] The else:
clause above may also be omitted. In that case, if none of
the conditions is true, no indented block is executed.

6. while statements [Simple while Loops]

while condition :
indentedStatementBlock

Do the indented block if condition is True , and at the end of the


indented block loop back and test the condition again, and
continue repeating the indented block as long as the condition is
True after completing the indented block. Execution does not stop
in the middle of the block, even if the condition becomes False at
that point.

A while loop can be used to set up an (intentionally) apparently


infinite loop by making condition be just True . To end the loop in
that case, there can be a test inside the loop that sometime
becomes True, allowing the execution of a return statement to
break out of the loop. [Fancier Animation Loop Logic (Optional)]

7. range function with three parameters [Simple while Loops]

range( start , pastEnd , step )

Return a list of elements

[ start , start + step , ... ]

with each element step from the previous one, ending just before
reaching pastEnd. If step is positive, pastEnd is larger than the last
element. If step is negative, pastEnd is smaller than the last
element.

8. Type tuple

( expression , expression , and so on )


( expression , )
( )

1. A literal tuple, with two or more elements, consists of a


comma separated collection of values all enclosed in
parentheses. A literal tuple with only a single element must
have a comma after the element to distinguish from a regular
parenthesized expression. [Loops and Tuples]
2. A tuple is a kind of sequence.
3. Tuples, unlike lists, are immutable (may not be altered).
« 9. Additional programming techniques

1. These techniques extend the techniques listed in the


summary of the previous chapter. [Summary]

2. The basic pattern for programming with a while loop is


[Simple while Loops]

initialization
while continuationCondition :
main action to repeat
prepare variables for next time through loop

3. Interactive while loops generally follow the pattern [Interactive


while Loops]

input first data from user


while continationConditionBasedOnTestOfUserData :
process user data
input next user data

Often the code to input the first data and the later data is the
same, but it must appear in both places!

4. Sentinel Loops [Interactive while Loops]

Often the end of the repetition of a data-reading loop is


indicated by a sentinel in the data: a data value known to
both the user and the program to not be regular data, that is
specifically used to signal the end of the data.

5. Nesting Control Flow Statements [Nesting Control-Flow


Statements]

1. Ifstatements may be nested inside loops, so the loop


does not have to execute all the same code each time;
it just needs to start with the same test.
2. Loops may be nested. The inner loop completes its
repetitions each time before going back to the outer
loop heading.
6. Breaking a repeating pattern into a loop [Graphical
Applications]

Since a loop is basically circular, there may be several


choices of where to split it to list it in the loop body. The split
point needs to be where the continuation test is ready to be
run, but that may still allow flexibility. When you choose to
change the starting point of the loop, and rotate statements
between the beginning and the end of the loop, you change
what statements need to be included before and after the
loop, sometimes repeating or undoing actions taken in the
loop.

7. Tuples in lists [Loops and Tuples] A list may contain tuples. A


for -each loop may process tuples in a list, and the for loop
heading can do multiple assignments to variables for each
element of the next tuple.

8. Tuples as return values [Loops and Tuples] A function may


return more than one value by wrapping them in a tuple. The
function may then be used in a multiple assignment
statement to extract each of the returned variables.

10. Graphics

1. Zelle’s Graphics GraphWin method checkMouse() allows


mouse tests without stopping animation, by testing the last
mouse click, not waiting for a new one. [Graphical
Applications]
2. The most finished examples of using Zelle’s graphics are in
[Loops and Tuples] and [Graphical Applications].
Hands-on Python Tutorial » 3. More On Flow of Control » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » previous | next | index

Previous topic 4. Dynamic Web Pages


3.6. Summary
4.1. Overview
Next topic 4.2. Web page Basics
4.1. Overview 4.2.1. Format of Web Page Markup
This Page 4.2.2. Introduction to Static Pages in Kompozer
4.2.3. Editing and Testing Different Document Formats
Show Source
4.3. Composing Web Pages in Python
Quick search 4.3.1. Dynamically Created Static Local Pages from Python
4.3.1.1. Quotient Web Exercise
4.4. CGI - Dynamic Web Pages
Go 4.4.1. An Example in Operation
4.4.2. A Simple Buildup
Enter search terms or a module,
4.4.3. Errors in CGI Scripts
class or function name.
« 4.4.3.1. Quotient.cgi Exercise
4.4.4. Editing HTML Forms
4.4.4.1. QuotientWeb Form Exercise
4.4.4.2. Dynamic Web Programming Exercise
4.4.5. More Advanced Examples
4.5. Summary

Hands-on Python Tutorial » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Jan 18, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 4. Dynamic Web Pages » previous | next | index

Previous topic 4.1. Overview


4. Dynamic Web Pages
This chapter leads up to the creation of dynamic web pages. These
Next topic pages and supporting programs and data may be tested locally via a
4.2. Web page Basics simple Python web server available on your local machine. If you have
This Page access, the pages and programs may be uploaded to a public server
accessible to anyone on the Internet.
Show Source
A few disclaimers:
Quick search
This tutorial does not cover uploading to an account on a public
Go
server.
No core Python syntax is introduced in this Chapter. Only a few
Enter search terms or a module, methods in a couple of Python library modules are introduced.
class or function name. The chapter is by no means a major source of information about
specific HTML codes. That is mostly avoided with the use of a
modern word-processor-like HTML editor. As a specific example,
the open source HTML editor Kompozer is discussed.

The chapter does allow you to understand the overall interaction


between a browser (like Firefox on your local machine) and a web
server and to create dynamic web content with Python. We treat
interaction with the web basically as a mechanism to get input into a
Python program and data back out and displayed. Web pages displayed
in your browser are used for both the input and the output. The
advantage of a public server is that it can also be used to store data
accessible to people all over the world.

There are a number of steps in the development in this chapter, so I start


with an overview:

1. A few bits about the basic format of hypertext markup language are
useful to start.
2. The simplest pages to start writing in Kompozer are just static web
pages, formatted like a word-processing document.
3. Next we look at pages generated dynamically. An easy way to
accomplish this is to create specialized static pages to act as
templates into which the dynamic data is easily embedded. Web
page creation can be tested totally locally, by creating HTML files
and pointing your web browser to them. Initially we supply input
data by our traditional means (keyboard input or function
parameters), and concentrate on having our Python program
convert the input to the desired output, and display this output in a
web page.
4. We generate data from within a web page, using web forms
(generated via Kompozer). Initially we will test web forms by
« automatically dumping their raw data.
5. To fully integrate a browser and server, we
1. use web forms to provide data
2. use a Python program specified on the server called a CGI
script to transform the input data into the desired output
3. embed the output in a new dynamic web page that gets sent
back to your browser. This Python server program
transforms the input data, and generates output web pages
much like we did in step 3, above.
6. Finally, if you set up a GoogleApp account, or have access to
another server, you can upload and show off your work on your
own personal web site, accessible to everyone on the Internet.
(Modifications needed are not covered in this Tutorial.)

Hands-on Python Tutorial » 4. Dynamic Web Pages » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 4. Dynamic Web Pages » previous | next | index

Table Of Contents 4.2. Web page Basics


4.2. Web page Basics
4.2.1. Format of Web
Page Markup
4.2.2. Introduction to 4.2.1. Format of Web Page Markup
Static Pages in
Kompozer Documents can be presented in many forms. A simple editor like Idle or
4.2.3. Editing and Windows’ Notepad produce plain text: essentially a long string of
Testing Different meaningful characters.
Document Formats

Previous topic Documents can be displayed with formatting of parts of the document.
Web pages allow different fonts, italic, and boldfaced emphases, and
4.1. Overview
different sized text, all to be included. Microsoft Word, Open Office, and
Next topic Latex, all display documents with various amounts of formatting. The
4.3. Composing Web Pages syntax for the ways different systems encode the formatting information
in Python varies enormously.
This Page If you look at an old Microsoft Word .doc document in a plain text editor
Show Source like Notepad, you should be able to find the original text buried inside,
but most of the symbols associated with the formatting are unprintable
Quick search gibberish as far as a human is concerned.

Hypertext markup language (HTML) is very different in that regard. It


Go
produces a file of entirely human-readable characters, that could be
Enter search terms or a module, produced with a plain text editor.
class or function name.
For instance in HTML, the largest form of a heading with the text “Web
Introduction”, would look like

<h1>Web Introduction</h1>

The heading format is indicated by bracketing the heading text ‘Web


Introduction’ with markup sequences, <h1> beforehand, and </h1>
afterward. All HTML markup is delimited by tags enclosed in angle
brackets, and most tags come in pairs, surrounding the information to be
formatted. The end tag has an extra ‘/’. Here ‘h’ stands for heading, and
the number indicates the relative importance of the heading. (There is
also h2, h3, .... for smaller headings.) In the early days of HTML, editing
was done in a plain text editor, with the tags being directly typed in by
people who memorized all the codes!

With the enormous explosion of the World Wide Web, specialized


software has been developed to make web editing be much like word
processing, with a graphical interface, allowing formatting to be done by
selecting text with a mouse and clicking menus and icons labeled in
more natural language. The software then automatically generates the
necessary markup. An example used in these tutorials is the open
source Kompozer, available at http://kompozer.net.
Warning: Careful - although this is free, open source software, the
URL is Kompozer.*net*, not Kompozer.org. There is a site
Kompozer.org that is designed to confuse you!)

You can open Kompozer and easily generate a document with a


heading, and italic and boldfaced portions....

4.2.2. Introduction to Static Pages in


Kompozer
This section introduces the Kompozer web page editor to create static
pages. A static page is one that is created ahead of time and just
opened and used as needed. This is as opposed to a dynamic page,
which is a custom page generated by software on demand,given some
input parameters.

Kompozer is used because it is free software, and is pretty easy to use,


like a common word processor. Unlike a common word processor you
will be able to easily look at the HTML markup code underneath. It is not
necessary to know a lot about the details of the markup codes for HTML
files to use Kompozer, but you can see the results of the markup.

We will use static pages later as a part of making dynamic pages, using
the static pages as templates in which we insert data dynamically.

To creating static web pages

1. If you are in a Loyola University Windows lab, follow the sequence


of selections Start menu Loyola software Math and Computer
Science Kompozer. (It may be under Internet instead of Math and
Computer Science instead.) You may get pop-up window wanting
to count users of Kompozer. Click OK as another user of
Kompozer.
2. However you start Kompozer, go to the menu in Kompozer and
select File New. You will get what looks like an empty document.
3. Look at the bottom of your window. You should see a Normal tab
selected, with other choices beside it, including a Source tab.
Click on the Source tab. You should see that, though you have
added no content, you already have the basic markup to start an
html page!
4. Click again on the Normal tab to go back to the Normal view (of no
content at the moment).
5. Assume you are making a home page for yourself. Make a title and
some introductory text. Use regular word processor features like
marking your title as Heading 1 in the drop down box on a menu
bar. (The drop down menu may start off displaying ‘Paragraph’ or
‘Body Text’.) You can select text and make it bold or italics;
enlarge it ... using the editing menu or icons.
6. Before getting too carried away, save your document as index.html
in the existing www directory under your earlier Python examples.
It will save a lot of trouble if you keep your web work together in
this www directory, where I have already placed a number of files
yo will want to keep together in one directory.
7. Just for comparison, switch back and forth between the Normal and
Source views to see all that has gone on underneath your view,
particularly if you edited the format of your text. Somewhere
embedded in the Source view you should see all the text you
entered. Some individual characters have special symbols in
HTML that start with an ampersand and end with a semicolon.
Again, it is more important the understand that there are two
different views than to be able to reproduce the Source view from
memory.
8. You can use your web browser to see how your file looks outside
the editor. The easiest way to do this is to go to the web browser’s
menu and select something like File Open File, and find the
index.html file you just wrote. It should look pretty similar to the
way it looked in Kompozer, but if you put in hyperlinks, they should
now be active.

The discussion of web page editing continues in Editing HTML Forms,


but first we get Python into the act.

4.2.3. Editing and Testing Different Document


Formats
Note: In this chapter you will be working with several different types
of documents that you will edit and test in very different ways. The
ending of their names indicate their use.

« Each time a new type of file is discussed in later sections, the proper
ways to work with it will be repeated, but with all the variations, it is
useful to group them all in one place now:

...Web.py
My convention for regular Python programs taking all their input from
the keyboard, and producing output displayed on a web page.
These programs can be run like other Python programs, directly
from an operating system folder or from inside Idle. They are not a
final product, but are a way of breaking the development process
into steps.
...cgi
Python program to be run by a web server. You will develop code
using a local web server on your own machine.
...html
Web documents most often composed in an editor like Kompozer.
By my convention, these have a sub-categories

...Template.html
not intended to be displayed directly in a browser, but instead
are read by a Python program (...cgi or ...Web.py) to create a
template or format string for a final web page that is dynamically
generated inside the Python program.

Other files ending in .html are intended to be directly viewed in a web


browser. Except for the simple static earlier examples in Introduction
to Static Pages in Kompozer, they are designed to reside on a web
server, where they can pass information to a Python CGI program
(...cgi).

To make this work on your computer:

1. Have all the web pages in the same directory as the example
program localCGIServer.py. It is easiest to leave it in the www
subdirectory of your examples directory.)
2. Include the Python CGI server programs in the same directory.
3. Have localCGIServer.py running, started from a directory window,
not from inside Idle
4. In the browser URL field, the web page file name must be preceded
by http://localhost:8080/. For example,
http://localhost:8080/adder.html would refer to the file adder.html,
in the same directory as the running localCGIServer.py. The URL
may either by an html file or possibly a CGI file. For example,
http://localhost:8080/now.cgi would call the file now.cgi (assuming
it is in the same directory as the running localCGIServer.py).
5. Most often CGI programs are referenced in a web form, and the
program is called indirectly by the web server. CGI programs can
be edited and saved inside Idle, but they do not run properly from
inside Idle. They must be run via the server/browser combination.
More on this later.
Hands-on Python Tutorial » 4. Dynamic Web Pages » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 24, 2013. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 4. Dynamic Web Pages » previous | next | index

Table Of Contents 4.3. Composing Web Pages in Python


4.3. Composing Web Pages
in Python
4.3.1. Dynamically
Created Static Local 4.3.1. Dynamically Created Static Local Pages from
Pages from Python
4.3.1.1. Quotient
Python
Web Exercise
For the rest of this chapter, the example files will come from the www directory
Previous topic under the main examples directory you unzipped. I will refer to example file there
4.2. Web page Basics as “example www files”.

Next topic As the overview indicated, dynamic web applications typically involve getting input
4.4. CGI - Dynamic Web from a web page form, processing the input in a program on the server, and
Pages displaying output to a web page. Introducing all these new ideas at once could be a
lot to absorb, so this section treats only the last part, output to the web, and uses
This Page
familiar keyboard input into a regular Python program.
Show Source
Follow this sequence of steps:
Quick search
1. Open the example www file hello.html in your browser, to see what it looks
Go
like.
2. Change your browser view - for instance go back to the previous page you
Enter search terms or a module, displayed.
class or function name. 3. Open the same hello.html file in Kompozer.
4. In Kompozer, switch to the Source view (clicking the Source tab). Sometimes
you will want to copy HTML text into a Python program. For instance, I
selected and copied the entire contents of the hello.html source view and
pasted it into a multi-line string in the Python program shown and discussed
below.
5. Careful, note the change from past practice here: Start Python from inside the
www directory. In Windows start the Idle shortcut link that I placed in the www
directory, not the original example directory.
6. Open the www example program helloWeb1.py in an Idle edit window.
7. Run it.

You should see a familiar web page appear in your default browser (possibly not
the one you have been using). This is obviously not a very necessary program,
since you can select this page directly in your browser! Still, one step at a time: it
illustrates several useful points. The program is copied below. Read it:

'''A simple program to create an html file froma given string,


and call the default web browser to display the file.'''

contents = '''<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01


Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title>Hello</title>
</head>
<body>
Hello, World!
</body>
</html>
'''

def main():
browseLocal(contents)

def strToFile(text, filename):


"""Write a file with the given name and the given text."""
output = open(filename,"w")
output.write(text)
output.close()

def browseLocal(webpageText, filename='tempBrowseLocal.html'):


'''Start your webbrowser on a local file containing the text
with given filename.'''
import webbrowser, os.path
strToFile(webpageText, filename)
webbrowser.open("file:///" + os.path.abspath(filename)) #elaborated
for Mac

main()

This program encapsulates two basic operations into the last two functions that will
be used over and over. The first, strToFile , has nothing new, it just puts specified
text in a file with a specified name. The second, browseLocal , does more. It takes
specified text (presumably a web page), puts it in a file, and directly displays the file
in your web browser. It uses the open function from the webbrowser module to start
the new page in your web browser. The open function here requires the name of a
file or URL. Since the page is automatically generated by the program for one-time
immediate viewing, it automatically uses the same throwaway filename,
tempBrowseLocal.html .

In this particular program the text that goes in the file is just copied from the literal
string named contents in the program.

This is no advance over just opening the file in the browser directly! Still, it is a start
towards the aim of creating web content dynamically.

An early example in this tutorial displayed the fixed Hello World!' to the screen.
This was later modified in hello_you4.py to incorporate user input using the string
format method of Dictionaries and String Formatting,

person = input('Enter your name: ')


greeting = 'Hello {person}!'.format(**locals())
print(greeting)

Similarly, I can turn the web page contents into a format string, and insert user data.
Load and run the www example program helloWeb2.py .

The simple changes from helloWeb1.py are marked at the beginning of the file and
shown below. I modified the web page text to contain ‘Hello, {person}!’ in place of
‘Hello, World!’, making the string into a format string, which I renamed to the more
appropriate pageTemplate . The changed initial portion with the literal string and and
the main program then becomes

pageTemplate = '''
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title>Hello</title>
</head>
<body>
Hello, {person}!
</body>
</html>''' # NEW note '{person}' two lines up

def main(): # NEW


person = input("Enter a name: ")
contents = pageTemplate.format(**locals())
browseLocal(contents)

Now the line

contents = pageTemplate.format(**locals())

incorporaties the person’s name into the contents for the web page before saving it
to a file and displaying it.

In this case, I stored the literal format string inside the Python program, but consider
a different approach:

Load and run the www example program helloWeb3.py . It behaves exactly like
helloWeb2.py, but is slightly different internally - it does not directly contain the web
page template string. Instead the web page template string is read from the file
helloTemplate.html .

Below is the beginning of helloWeb3.py, showing the only new functions. The first,
fileToStr , will be a standard function used in the future. It is the inverse of
strToFile .

The main program obtains the input. In this simple example, the input is used
directly, with little further processing. It is inserted into the web page, using the file
helloTemplate.html as a format string.

def fileToStr(fileName): # NEW


"""Return a string containing the contents of the named file."""
fin = open(fileName);
contents = fin.read();
fin.close()
return contents

def main():
person = input('Enter a name: ')
contents = fileToStr('helloTemplate.html').format(**locals()) # NEW
browseLocal(contents)

Although helloTemplate.html is not intended to be viewed by the user (being a


template), you should open it in a web editor (like Kompozer) to look at it. It is legal
to create a web page in a web page editor with expressions in braces embedded in
it! If you look in the source view in Kompozer you will see something similar to the
literal string in helloWeb2.py, except the lines are broken up differently. (This
makes no difference in the formatted result, since in html, all white space is
considered the same.)

Back in the Normal mode, add some formatting like italics, and an extra line of text,
and save the file again (under the same name). Run the program helloWeb3.py
again, and see that you have been able to change the appearance of the output
without changing the Python program itself. That is the aim of using the template
html page, allowing the web output formatting to be managed mostly independently
from the Python program.

A more complicated but much more common situation is where the input data is
processed and transformed into results somehow, and these results, often along
with some of the original input, are embedded in the web page that is produced.

As a simple example, load and run the www example program additionWeb.py ,
« which uses the template file additionTemplate.html .

The aim in the end of this chapter is to have user input come from a form on the
web rather than the keyboard on a local machine, but in either case the input is still
transformed into results and all embedded in a web page. To make parts easily
reusable, I obtain the input in a distinct place from where the input is processed. In
keeping with the later situation with web forms, all input is of string type ( using
keyboard input for now).

Look at the program. You will see only a few new lines! Because of the modular
design, most of the program is composed of recent standard functions reused.

The only new code is at the beginning and is shown here:

def processInput(numStr1, numStr2): # NEW


'''Process input parameters and return the final page as a string.'''
num1 = int(numStr1) # transform input to output data
num2 = int(numStr2)
total = num1+num2
return fileToStr('additionTemplate.html').format(**locals())

def main(): # NEW


numStr1 = input('Enter an integer: ') # obtain input
numStr2 = input('Enter another integer: ')
contents = processInput(numStr1, numStr2) # process input into a
page
browseLocal(contents) # display page

The input is obtained (via input for now), and it is processed into a web page string,
and as a separate step it is displayed in a local web page.

There are a few things to note:

All input is strings. Before the numerical calculations, the digit strings must be
converted to integers.
I do calculate (a very simple!) result and use it in the output web page.
Although it is not in the Python code, an important part of the result comes
from the web page format string in additionTemplate.html, which includes the
needed variable names in braces, {num1}, {num2}, and {total}. View it in your
browser or in Kompozer.

When you write your own code, you might modify additionWeb.py , or you can start
from a stripped down skeleton with comments about where to insert your special
code, skeletonForWeb.py .

We will examine the bottom part of the following diagram later. The top part outlines
the flow of data from string input to web page in your browser for a regular Python
program like what we have been describing.
Again, this last section was somewhat artificial. You are not in the end likely to find
such programs practical as end products. However such programs are reasonable
to write and test and they include almost all the code you will need for a more
practical (but harder to debug) CGI program, coming next....

4.3.1.1. Quotient Web Exercise


* Save additionWeb.py or skeletonForWeb.py as quotientWeb.py . Modify it to display
the results of a division problem in a web page. As in the exercises in Chapter 1,
display a full sentence labeling both the integer quotient and the remainder. You
can take your calculations from Quotient String Return Exercise. You should only
need to make Python changes to the processInput and main functions. You will also
need the HTML for the page displayed. Make a web page template file called
quotientTemplate.html and read it into your program.
Hands-on Python Tutorial » 4. Dynamic Web Pages » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Jan 18, 2014. Created using Sphinx 1.2b1.
.. Hands-on Python Tutorial documentation master file, created by
sphinx-quickstart on Mon Jan 10 09:06:11 2011.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.

Hands-on Python Tutorial


========================

Python 3.1 Version

Dr. Andrew N. Harrington

Computer Science Department, Loyola University Chicago

© Released under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
http://creativecommons.org/licenses/by-nc-sa/3.0/us/

---------------

.. toctree::
:maxdepth: 2
:numbered:

ch1
ch2
ch3
ch4

:ref:`genindex`
Hands-on Python Tutorial » 4. Dynamic Web Pages » previous | next | index

Table Of Contents 4.4. CGI - Dynamic Web Pages


4.4. CGI - Dynamic Web
Pages CGI stands for Common Gateway Interface. This interface is used by web servers to
4.4.1. An Example in process information requests supplied by a browser. Python has modules to allow
Operation
programs to do this work. The convention used by many servers is to have the server
4.4.2. A Simple Buildup
4.4.3. Errors in CGI programs that satisfy this interface end in ‘.cgi’. That is the convention used below. All
Scripts files below ending in ‘.cgi’ are CGI programs on a web server, and in this chapter, they
4.4.3.1. will all be Python programs (though there are many other languages in use for this
Quotient.cgi purpose). These programs are often called scripts, so we will be dealing with Python
Exercise CGI scripts.
4.4.4. Editing HTML
Forms
4.4.4.1.
QuotientWeb
Form Exercise
4.4.1. An Example in Operation
4.4.4.2. Dynamic
Web Programming The first part of this section requires you to have access to the Internet. Later you will
Exercise also see that you can illustrate the exact same actions on your own local machine.
4.4.5. More Advanced
Examples For a very simple but complete example, use your browser to go to the page on the
public Loyola server, http://anh.cs.luc.edu/python/hands-
Previous topic on/3.1/examples/www/adder.html. You see a web form. Follow the instructions, enter
4.3. Composing Web Pages numbers, and click on the Find Sum button. You get back a page that obviously used
in Python
your data. This is the idea that you can generalize. First consider the basic execution
Next topic steps behind the scene:
4.5. Summary
1. The data you type is handled directly by the browser. It recognizes forms.
This Page 2. An action instruction is stored in the form saying what to do when you press a
Show Source button indicating you are ready to process the data (the Find Sum button in this
case).
Quick search 3. In the cases we consider in this tutorial, the action is given as a web resource,
giving the location of a CGI script on some server (in our cases, the same directory
on the server as the current web page).
Go
4. When you press the button, the browser sends the data that you entered to that
Enter search terms or a module,
web location (in this case adder.cgi in the same folder as the original web page).
class or function name. 5. The server recognizes the web resource as an executable script, sees that it is a
Python program, and executes it, using the data sent along from the browser form
as input.
6. The script runs, manipulates its input data into some results, and puts those results
into the text of a web page that is the output of the program.
7. The server captures this output from the program and sends it back to your browser
as a new page to display.
8. You see the results in your browser.

This also works locally, entirely on your own computer, using a simple server built into
Python. (Internet no longer needed!)

Windows
In an operating system file window, go to the folder with the www examples. Double
click on localCGIServer.py to start the local, internal, web server. You should see a
console window pop up, saying “Localhost CGI server started” .
Mac
This is more involved. See http://anh.cs.luc.edu/python/hands-
on/3.1/pythonOnMac.html.
Once the server is started, leave the console window there as long as you want the local
server running.

Warning: Do not start the local server running from inside Idle.

Note: If the server aborts and gives an error message about spaces in the path, look
at the path through the parent directories over this www directory. If any of the
directory names have spaces in them, the local file server will not work.

In case of this error, either go up the directory chain and alter the directory names to
eliminate spaces or move the examples directory to a directory that does not have this
issue. In particular, you need to move your examples directory if it is under the ‘My
Programs’ directory.

Back in the www directory, after you have the local server going,

1. Open the web link http://localhost:8080/adder.html (preferably in a new window,


separate from this tutorial).
2. You should see an adder form in your browser again. Note that the web address no
longer includes ‘cs.luc.edu’. Instead it starts with ‘localhost:8080’, to reference the
local Python server you started. Fill out the form and test it as before.
3. Look at the console window. You should see a log of the activity with the server.
Close the server window.
4. Reload the web link http://localhost:8080/adder.html. You should get an error, since
you refer to localhost, but you just stopped the local server.

For the rest of this chapter, we will be wanting to use the local server, so restart
localCGIServer.py from its operating system folder, and keep it going.

4.4.2. A Simple Buildup


Before we get too complicated, consider the source code of a couple of even simpler
examples.

hellotxt.cgi

The simplest case is a CGI script with no input that just generates plain text, rather than
HTML. Assuming you have your local server going, you can go to the link for hellotxt.cgi,
http://localhost:8080/hellotxt.cgi. The code is in the www example directory,
hellotxt.cgi , and below for you to read:

#!/usr/bin/env python3

# Required header that tells the browser how to render the text.
print("Content-Type: text/plain\n\n") # here text -- not html

# Print a simple message to the display window.


print("Hello, World!\n")

The top line is what tells the operating system that this is a Python 3 program. It says
where to find the right Python interpreter to process the rest of the script. This exact
location is significant on a Unix derived server (like the one Loyola’s Computer Science
Department uses or any Mac with OS X). In Windows the only thing important is the
distinction between Python 2 and 3. If you leave the line there as a part of your standard
text, you have one less thing to think about when uploading to a Unix server or running
on a Mac.
The first print function is telling the server receiving this output that the format of the rest
of the output will be plain text. This information gets passed back to the browser later.
This line should be included exactly as stated IF you only want the output to be plain text
(the simplest case, but not our usual case).

The rest of the output (in this case just from one print function) becomes the body of the
plain text document you see on your browser screen, verbatim since it is plain text. The
server captures this output and redirects it to your browser.

hellohtml.cgi

We can make some variation and display an already determined html page rather than
plain text. Try the link http://localhost:8080/hellohtml.cgi. The code is in the www
example directory, hellohtml.cgi , and below for you to read:

#!/usr/bin/env python3

print("Content-Type: text/html\n\n") # html markup follows

print("""
<html>
<Title>Hello in HTML</Title>
<body>
<p>Hello There!</p>
<p><b>Hi There!</b></p>
</body>
</html> """)

There are two noteworthy changes. The first print function now declares the rest of the
output will be html. This is the standard line you will be using for your CGI programs. The
remaining print function has the markup for an html page. Note that the enclosing triple
quotes work for a multi line string. Other than as a simple illustration, this CGI script has
no utility: Just putting the contents of the last print function in a file for a static web page
hello.html is much simpler.

now.cgi

One more simple step: we can have a CGI script that generates dynamic output by
reading the clock from inside of Python: Try the link http://localhost:8080/now.cgi. Then
click the refresh button and look again. This cannot come from a static page. The code is
in the www example directory, now.cgi , and below for you to read:

#!/usr/bin/env python3

import time
print("Content-Type: text/html\n\n") # html markup follows

timeStr = time.strftime("%c") # obtains current time

htmlFormat = """
<html>
<Title>The Time Now</Title>
<body>
<p>The current Central date and time is: {timeStr}</p>
</body>
</html> """

print(htmlFormat.format(**locals())) # see embedded %s ^ above

This illustrates a couple more ideas: First a library module, time , is imported and used to
generate the string for the current date and time.

The web page is generated like in helloWeb2.py , embedding the dynamic data (in this
case the time) into a literal web page format string. (Note the embedded {timeStr} .)
Unlike helloWeb2.py , this is a CGI script so the web page contents are delivered to the
server just with a print function.

adder.cgi

It is a small further step to get to processing dynamic input. Try filling out and submitting
the adder form one more time, http://localhost:8080/adder.html. This time notice the URL
at the top of the browser page when the result is displayed. You should see something
like the following (only the numbers should be the ones you entered):

http://localhost:8080/adder.cgi?x=24&y=56

This shows one mechanism to deliver data from a web form to the CGI script that
processes it. The names x and y are used in the form (as we will see later) and the data
you entered is associated with those names. In fact a form is not needed at all to create
such an association: If you directly go to the URLs

http://localhost:8080/adder.cgi?x=24&y=56

or

http://localhost:8080/adder.cgi?x=-12345678924&y=33333333333

you get arithmetic displayed without the form. This is just a new input mechanism into the
CGI script.

You have already seen a program to produce this adder page from inside a regular
Python program taking input from the keyboard. The new CGI version, adder.cgi, only
needs to make a few modifications to accept input this way from the browser. New
features are commented in the source and discussed below. The new parts are the
import statement through the main function, and the code after the end of the fileToStr
function. Read at least these new parts in the source code shown below:

#!/usr/bin/env python3

import cgi # NEW

def main(): # NEW except for the call to processInput


form = cgi.FieldStorage() # standard cgi script lines to here!

# use format of next two lines with YOUR names and default data
numStr1 = form.getfirst("x", "0") # get the form value associated with form
# name 'x'. Use default "0" if there is
none.
numStr2 = form.getfirst("y", "0") # similarly for name 'y'
contents = processInput(numStr1, numStr2) # process input into a page
print(contents)

def processInput(numStr1, numStr2):


'''Process input parameters and return the final page as a string.'''
num1 = int(numStr1) # transform input to output data
num2 = int(numStr2)
total = num1+num2
return fileToStr('additionTemplate.html').format(**locals())

# standard code for future cgi scripts from here on


def fileToStr(fileName):
"""Return a string containing the contents of the named file."""
fin = open(fileName);
contents = fin.read();
fin.close()
return contents

try: # NEW
print("Content-type: text/html\n\n") # say generating html
main()
except:
cgi.print_exception() # catch and print errors

First the overall structure of the code:

To handle the CGI input we import the cgi module.


The main body of the code is in a main method, following good programming
practice.
After the definition of main come supporting functions, each one copied from the
earlier local web page version, additionWeb.py.
At the end is the new, but standard, cgi wrapper code for main() . This is code that
you can always just copy. I chose to put the initial print function here, that tells the
server html is being produced. That mean the main method only needs to construct
and print the actual html code. Also keep the final try - except block that catches
any execution errors in the program and generates possibly helpful trace
information that you can see from your browser. (Writing such error catching code
in general is not covered in this introductory tutorial, but you can copy it!)

The main function has three sections, as in the local web page version: read input (this
time from the form), process it, and generate the html output.

Reading input: The first line of main is a standard one (to copy) that sets up an
object called form that holds the CGI form data accompanying the web request
sent by the browser. You access the form data with statements like the next two
that have the pattern:

variable = form.getfirst( nameAttrib , default )

If there is a form field with name nameAttrib, its value from the browser data is
assigned to variable. If no value is given in the browser’s data for nameAttrib,
variable is set equal to default instead.

In this way data associated with names given by the browser can transferred to
your Python CGI program. In this program the values associated with the browser-
supplied names, ‘x’ and ‘y’, are extracted. I use Python variable names that remind
you that all values from the browser forms are strings.

The processInput function that is passed the input parameters from whatever
source, is exactly the same as in additionWeb.py , so we already know it works!

Output the page. In a CGI script this is easier than with the local web pages: just
print it - no need to save and separately display a file! The server captures the
“printed” output.

This program can now serve as a template for your own CGI scripts: The only things you
need to change are the lines in main() that get the input from a web form, and the
contents of processInput . Furthermore the processInput part can be written and tested
earlier with a local web page. While this is the only Python code, you still need to create
an output web page template, and refer to it in the parameter of fileToStr . A further
stripped down skeleton, with comments about needed changes is in skeletonFor.cgi .
Now we have discussed both the top regular Python sequence, the bottom cgi sequence,
and the common part in the middle, as shown in the following diagram. In both cases
input data gets processed into the content of a web page that goes to a browser. For any
major application the main work is in the processing in the middle. Since that part is
shared in both approaches, it can be tested with a simple Python program, before the
starting and ending steps for the input and output flow are changed for the cgi
client/server model.

The only part that still needs details explained is for web forms. Before going on to that, it
is time to talk about handling errors in the CGI scripts we have been discussing.

4.4.3. Errors in CGI Scripts


Before you start running your own CGI scripts on the local server, it is important to
understand how different kinds of errors that you might make will be handled.

If you write a regular Python program, even one that produces a web page, you can write
the code and run it in Idle, and idle will display all the kinds of errors.

With a CGI script, you can still use Idle to write your code, but you cannot run the cgi
code in Idle, and errors show up in three different places, depending on their type:

Syntax errors
You are encouraged to check for syntax errors inside Idle, by either going to the Run
menu and selecting Check Module, or by using the shortcut Alt-X . If you fail to do
this and try running a script with a syntax error, the error trace appears in the
console window that appears when you start the local server. If you want an
illustration, you might try changing adder.cgi, making an error like impor cgi , and try
using adder.html with the flawed script. (Then fix it and try again.)
Execution Errors
The error trace for execution errors is displayed in your web browser, thanks to the
final standard code with the try - catch block at the end of the CGI script. If you omit
that final standard code, you completely lose descriptive feedback: Be sure to
include that standard code! You can also illustrate here. Get an error by introducing
the statement:

bad = 'a'.append('b')

in the main function. (Then take it out.)

Server Errors
Your work can cause an issue inside the local server, not directly in the Python
execution. Some errors are communicated to the browser, but not necessarily all.
Other errors appear in the log generated in the local server’s window. It does not
appear likely that you will miss something in Windows, but on a Mac or in Linux,
where the CGI script needs to be set as executable, an error with a non-executable
CGI script only shows up in this log in the local server window.
Logical Errors
Since your output appears in the web browser, when you produced something legal
but other than what you intended, you see in the browser . If it is a formatting error,
fix your output page template. If you get wrong answers, check your processInput
function.

Here is an outline for client/server program testing, emphasizing errors to be conscious of


and avoid:

If you want an easy environment to test a fancy processInput function, embed it in a


regular Python program, so you can test it normally in Idle. This will also allow you
to make sure the web template, that you refer to in your processInput function, is in
a legitimate form, with substitutions only for local variables.
You can code a CGI script in idle, but not run it. Be sure to save it with the suffix
.cgi , not .py . Do not run it it Idle. The only testing you can do in Idle is for syntax,
for instance using the Alt-X keyboard shortcut.
At the end of your CGI script, make sure you include the standard code that
catches execution errors.
Make sure your local server is going, and that all the files you reference are in the
same folder as the local server.
Make sure you test your page by starting it in your web browser with a URL starting
http://localhost:8080/ . If you load a web page directly from your file system by
mistake, it will not cause an obvious error - the dynamic actions will just not take
place. If you are not paying attention, this can happen and be very confusing!

We have not covered web forms yet, but rather than bite off too much at once, this is a
good time to write your own first CGI script in the following exercise.

4.4.3.1. Quotient.cgi Exercise


* Modify Quotient Web Exercise and save it as a CGI script quotient.cgi in the same
directory where you have localCGIServer.py and your output page template for
quotientWeb.py . Make quotient.cgi take its input from a browser, rather than the
keyboard. This means merging all the standard CGI code from adder.cgi and the
processInput function code from your quotientWeb.py . You can keep the same browser
data names, x and y, as in adder.cgi, so the main method should not need changes from
adder.cgi. Remember to test for syntax errors inside Idle, and to have the local web
server running when you run the CGI script in your browser. Since we have not yet
covered web forms, test your CGI script by entering test data into the URL in the
browser, like by going to links http://localhost:8080/quotient.cgi?x=24&y=56 and
http://localhost:8080/quotient.cgi?x=36&y=15. After trying these links, you can edit the
numbers in the URL in the browser to see different results.

4.4.4. Editing HTML Forms


This section is a continuation of Introduction to Static Pages in Kompozer. It is about
HTML editing, not Python. HTML forms will allow user-friendly data entry for Python CGI
scripts. This is the last elaboration to allow basic web interaction: Enter data in a form,
submit it, and get a processed result back from the server.

The initial example, adder.html, used only two text fields. To see more common form
fields, open http://localhost:8080/commonFormFields.html. (Make sure your local server
is still running!)

To allow easy concentration on the data sent by the browser, this form connects to a
simple CGI script dumpcgi.cgi , that just dumps and labels all the form data to a web
page. Press the submit button in the form, and see the result. Back up from the output to
the previous page, the form, and change some of the data in all kinds of fields. Submit
again and see the results. Play with this until you get the idea clearly that the form is
passing on your data.

To play with it at a deeper level, open this same file, the www example
commonFormFields.html , in Kompozer. The static text in this page is set up as a tutorial on
forms in Kompozer. Read the content of the page describing how to edit the overall form
and each type of individual field. Textbooks such as the Analytical Engine give another
discussion of some of the attributes associated with each field type. Read the static text
about how to edit individual fields, and change some field parameters, save the file and
reload it in your browser, and submit again. If you change the name or value attributes,
they are immediately indicated in the dumped output. If you change things like the text
field size, it makes a change in the way the form looks and behaves. You can return to
the original version: An extra copy is saved in commonFormFieldsOrig.html .

Now open adder.html in Kompozer. Switch to the Source view. This is a short enough
page that you should not get lost in the source code. The raw text illustrates another
feature of html: attributes. The tag to start the form contains not only the tag code form,
but also several expressions that look like Python assignment statements with string
values. The names on the left-hand side of the equal signs identify a type of attribute,
and the string value after the equal sign gives the corresponding value for the attribute.
The tag for many kinds of input fields is input . Notice that each field includes name and
value attributes. See that the ‘x’ and ‘y’ that are passed in the URL by the browser come
from the names given in the HTML code for the corresponding fields!

Kompozer and other web editors translate your menu selections into the raw html code
with proper attribute types. This high level editor behavior is convenient to avoid having
to learn and debug the exact right html syntax! On the other hand, using pop-up field
editing windows has the disadvantage that you can only see the attributes of one field at
a time. Particularly if you want to modify a number of name or value attributes, it is
annoying that you need a number of mouse clicks to go from one field to the next. If you
only want to modify the values of existing attributes like name and value , it may be easier
to do in the source window, where you can see everything at once. Making syntax errors
in not very likely if you only change data in quoted value strings.

The action URL is a property of the entire form. To edit it in Kompozer, right click inside
the form, but not on any field element, and select the bottom pop-up choice, Form
Properties. Then you see a window listing the Action URL and you can change the value
« to the name of the CGI script that you want to receive the form data. When you create
your own web form, I suggest you make the initial action URL be dumpcgi.cgi. This will
allow you to debug your form separate from your CGI script. When you have tested that
your web form has all the right names and initial values, you can change the action URL
to your CGI script name (like quotient.cgi), and go on to test the combination of the form
and the CGI script!

Now we have discussed the last piece, web forms, in the diagram for the comparison of
generating web pages dynamically by a regular Python program or a server CGI script:
Note the last three Python videos do not directly corresponding to a single place in the
Tutorial text. Instead they go through the entire process for web based programs from
the beginning. Video 4.4.4b creates a birthday.html web form looking forward to
birthday.cgi of video 4.4.4d. In the middle video 4.4.4c creates birthdayWeb.py , testing
the process function and output template to be used in birthday.cgi .

4.4.4.1. QuotientWeb Form Exercise


* Complete the web presentation for quotient.cgi of Quotient.cgi Exercise by creating a
web form quotient.html that is intelligible to a user and which supplies the necessary
data to quotient.cgi .

Be sure to test the new form on your local server! Remember that you must have the
local server running first. You must have all the associated files in the same directory as
the server program you are running, and you cannot just click on quotient.html in a file
browser. You must start it from the the URL http://localhost:8080/quotient.html, that
specifically refers to the server localhost.

4.4.4.2. Dynamic Web Programming Exercise


* Make a simple complete dynamic web presentation with a CGI script that uses at least
three user inputs from a form. The simplest would be to just add three numbers instead
of two. Call your form dynamic.html . Call your CGI script dynamic.cgi . Call an output
template dynamicTemplate.html . Remember the details listed in the previous exercise to
make the results work on localhost. After the server is started and you have all the files,
go to http://localhost:8080/dynamic.html.

Summary starts with the overall process for creating dynamic web pages.

4.4.5. More Advanced Examples


One of the advantages of having a program running on a public server is that data may
be stored centrally and augmented and shared by all. In high performance sites data is
typically stored in a sophisticated database, beyond the scope of this tutorial. For a less
robust but simpler way to store data persistently, we can use simple text files on the
server.

The www example page namelist.html uses namelist.cgi to maintain a file namelist.txt
of data submitted by users of the page. You can test the program with your local Python
server. It is less impressive when you are the only one who can make changes! You
may also try the copy on the public Loyola server, http://anh.cs.luc.edu/python/hands-
on/3.1/examples/www/namelist.html. The local source code is documented for those who
would like to have a look.

You also may want to look at the source code of the utility script you have been using,
dumpcgi.cgi . It uses a method of getting values from the CGI data that has not been
discussed:

val = form.getlist(name)

This method returns a list of values associated with a name from the web form. The list
many have, 0, 1, or many elements. It is needed if you have a number of check boxes
with the same name. (Maybe you want a list of all the toppings someone selects for a
pizza.)

Both dumpcgi.cgi and namelist.html add an extra layer of robustness in reflecting back
arbitrary text from a user. The user’s text may include symbols used specially in html like
‘<’. The function safePlainText replaces reserved symbols with appropriate alternatives.

The examples in earlier sections were designed to illustrate the flow of data from input
form to output page, but neither the html or the data transformations have been very
complicated. A more elaborate situation is ordering pizza online, and recording the
orders for the restaurant owner. You can try http://localhost:8080/pizza1.cgi several
times and look at the supporting example www files pizza1.cgi ,
pizzaOrderTemplate1.html , and the simple pizzaReportTemplate.html . To see the report,
the owner needs to know the special name owner777 . After ordering several pizzas, enter
that name and press the Submit button again.

This CGI script gets used in two ways by a regular user: initially, when there is no order,
and later to confirm an order that has been submitted. The two situations use different
logic, and the script must distinguish what is the current use. A hidden variable is used to
distinguish the two cases: when pizza1.cgi is called directly (not from a form), there is no
pastState field. On the other hand the pizzaOrderTemplate1.html includes a hidden field
named pastState , which is set to the value 'order' . (You can confirm this by examining
the end of the page in Kompozer’s source mode.) The CGI script checks the value of the
field pastState , and varies its behavior based on whether the value is 'order' or not.

The form in pizzaOrderTemplate1.html has radio buttons and check boxes hard coded
into it for the options, and copies of the data are in pizza1.cgi. Keeping multiple active
copies of data is not a good idea: They can get out of sync. If you look at the source
code for pizzaOrderTemplate1.html, you see that all the entries for the radio button and
check box lines are in a similar form. In the better version with altered files pizza.cgi and
pizzaOrderTemplate.html (that appears the same to the user), the basic data for the pizza
options is only in one place in pizza.cgi , and the proper number of lines of radio buttons
and check boxes with the right data are generated dynamically. To do the dynamic
generation, a templates for an individual html line with a size radio button is in the source
code, and it is used repeatedly to generate multiple lines, each with a different size and
price embedded into the format string from the program data. These lines are joined
together and placed as one entity into the html form template. A similar procedure is
done with the toppings and checkboxes.

A further possible elaboration would be to also allow the restaurant manager to edit the
size, cost and available topping data online, and store the data in a file rather than
having the data hard coded in pizza.cgi, so if the manager runs out of a topping, she can
remove it from the order form. This change would be a fairly elaborate project compared
to the earlier exercises!
Hands-on Python Tutorial » 4. Dynamic Web Pages » previous | next | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Jan 18, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » 4. Dynamic Web Pages » previous | index

Previous topic 4.5. Summary


4.4. CGI - Dynamic Web
Pages 1. The Overall Process for Creating Dynamic Web Pages
This Page Making dynamic web pages has a number of steps. I have
Show Source suggested several ways of decoupling the parts, so you can alter
the order, but if you are starting from nothing, you might follow the
Quick search following sequence:

1. Determine the inputs you want to work with and make a web
Go
form that makes it easy and obvious for the user to provide
Enter search terms or a module, the data. You may initially want to have the form’s action
class or function name. URL be dumpcgi.cgi , so you can debug the form separately.
Test with the local server. When everything seems OK, make
sure to change the action URL to be the name of the CGI
script you are writing. [Editing HTML Forms]
2. It is easier to debug a regular Python program totally inside
Idle than to mix the Idle editor and server execution.
Particularly if the generation of output data is going to be
complicated or there are lots of places you are planning to
insert data into an output template, I suggest you write the
processInput function with its output template first and test it
without a server, as we did with additionWeb.py , providing
either canned input in the main program, or taking input data
from the keyboard, and saving the output page to a local file
that you examine in your webbrowser. [Dynamically Created
Static Local Pages from Python]
3. When you are confident about your processInput function, put
it in a program with the proper cgi skeleton, and add the
necessary lines at the beginning of the main function to take
all the CGI script input from the browser data. [adder.cgi]
4. Be sure to check for syntax errors in Idle, for instance using
Alt-X . Fix as necessary.
5. Finally test the whole thing with the local server. Make sure
the local server is running, and all the resources that you
refer to are in the same folder as the local web server: Initial
web page, web page templates, CGI script. Do not open the
starting web page or CGI script in Idle or by finding it in your
file system. You must run it in your browser with a URL that
starts with http://localhost:8080/ . In error, if you load a web
page directly from your file system, it will not cause an
obvious error - the dynamic actions will just not take place.
6. If is does not work right:
If you get a page that uses your template, but it looks
wrong, either fix your template or look for a logical error
in your program. (If you had tested your processInput
function in a regular Python program before, this should
not happen.)
If the web page output shows an error description, see if
you can pick any help out and go back and fix your
code.
If you get nothing back in your web browser, make sure
you had tested the final version of the code in Idle for
syntax errors ( Alt-X ), and that you have the final error
catching code in the CGI script, and that you used a
URL that starts with http://localhost:8080/ .
If all of the parts mentioned above are there, the
problem may be with the server, not Python. Look in
the local server window’s log output, and see if it points
to a filename that it cannot find or ....
7. If you have an account on a public server, it should not take
much more work than just uploading your files to make your
creation available to the whole world. You may have a public
server with a different configuration than the Loyola server. If
so see this note: [1]
2. Markup: Plain text may be marked up to include formatting. The
formatting may be easily interpreted only by a computer, or it may
be more human readable. One form of human-readable markup is
hypertext markup language (HTML). [Format of Web Page
Markup]

1. HTML markup involves tags enclosed in angle braces. Ending


tags start with ‘/’. For instance <title>Computer
Science</title> .

1. Tags may be modified with attributes specified similar to


Python string assignments, for example the text input
field tag,

<input value="red" name="color" type="radio">

2. Modern editors allow HTML to be edited much like in a word


processor. Two views of the data are useful: the formatted
view and the source view, showing the raw HTML markup.

3. Python and HTML: Since HTML is just a text string, it can easily be
manipulated in Python, and read and written to text files.
[Dynamically Created Static Local Pages from Python]

4. The webbrowser module has a function open , that will open a file or
web URL in the default browser: [Dynamically Created Static Local
Pages from Python]

webbrowser.open( filename )
5. Common Gateway Interface (CGI). The sequence of events for
generating a dynamic web page via CGI: [An Example in
Operation]

1. The data a user types is handled directly by the browser. It


recognizes forms.
2. The user presses a Submit button. An action is stored in the
form saying what to do when the button is pressed.
3. In the cases we consider in this tutorial, the action is given as
a web resource, giving the location of a CGI script on some
server. The browser sends the data that you entered to that
web location.
4. The server recognizes the page as an executable script, sees
that it is a Python program, and executes it, using the data
sent along from the browser form as input.
5. The script runs, manipulates the input data into some results,
and puts those results into the text of a web page that is the
output of the program.
6. The server captures this output from the program and send it
back to the user’s browser as a new page to display.
7. The results appear in the user’s browser.
6. The cgi Module

1. Create the object to process CGI input with [adder.cgi]

form = cgi.FieldStorage()

2. Extract the first value specified by the browser with name


nameAttrib, or use default if no such value exists [adder.cgi]

variable = form.getfirst( nameAttrib , default )

3. Extract the list of all values specified by the browser


associated with name nameAttrib [ref{More Advanced
«
Examples]

listVariable = form.getlist( nameAttrib )

This case occurs if you have a number of checkboxes, all with


the same name, but different values. The list may be empty.

7. Local Python Servers.

1. Python has modules for creating local testing servers that can
handle static web pages and Python CGI scripts.[An
Example in Operation]
2. Different kinds of errors with CGI scripts are handled different
ways by a local Python server. [Errors in CGI Scripts]
8. A comparison of the various types of files used in web
programming, listing the different ways to edit and use the files, is
given in Editing and Testing Different Document Formats.

[1] The tutorial assumed a server configured as follows: html pages


and CGI scripts can all be in the same directory, and the CGI
scripts end with .cgi. This is the convention on Loyola’s Computer
Science public server. Another common configuration is that
scripts all go in a cgi-bin directory, where they just have the
normal .py suffix. If you have a server with the latter configuration,
your action URLs will be of the form cgi-bin/someScript.py.
Depending on the server configuration the current directory may
or may not be cgi-bin while the script executes. That may mean
you need a path before the file names for your output templates,
or your need to be careful what directory referenced files end up
in. If you are making arrangements for your own site on a public
server, be sure to check with your system administrator to find out
what the conventions are.
Hands-on Python Tutorial » 4. Dynamic Web Pages » previous | index

© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.

You might also like