Hands-On Python Tutorial
Hands-On Python Tutorial
Loyola eCommons
Computer Science: Faculty Publications and Other
Faculty Publications
Works
2015
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
Index
© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Sep 08, 2014. Created using Sphinx 1.2b1.
Hands-on Python Tutorial » index
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
© 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
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.
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).
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:
Also mentioned for the convenience of my Comp 150 class are videos beyondPython, for the part of the class after Python.
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.
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!
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:
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
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: 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.
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.
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.
python madlib.py
python3 madlib.py
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.
#! /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
#! /usr/bin/env python3 0
''' 1
String Substitution for a Mad Lib 2
Adapted from code by Kirby Urner 3
''' 4
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() .
28: This is where all the work becomes visible: Print the story string to
the screen.
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.
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 .
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.
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.
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.
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:
>>>
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
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.
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
>>>
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])
len([2, 4, 6])
len('abcd')
list()
max(5, 11, 2)
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.
© 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
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.
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.)
-(2 + 3)
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.
14/4
14//4
14%4
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
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.
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 ):
The plus operation with strings means concatenate the strings. Python
looks at the type of operands before deciding what operation is
associated with the +.
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....
© 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
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
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.
height = 12
area = width * height
area
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.
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!
fred = 'Frederick'
first = fred
first
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.)
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 .
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:
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.
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
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.
© 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
sillyTest
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.
print('a\nb\n\nc')
Did you guess the right number of lines splitting in the right places?
© 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
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.
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.)
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:
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.
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.
On a Mac you get to explicitly close the terminal window created when
you run a Python program from the Finder.
Run the program and see the documentation and comment make no
difference in the result.
© 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
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:
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:
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]
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:
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]
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:
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.
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.
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.
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.
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:
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.
© 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
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
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.
After Idle finished executing a program, however, its version of the Shell
remembers function definitions from the program.
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 ()
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. 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.
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. :
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.
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 :
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]
4. Lines 4-7: The song is printed, with 'Emily' used as the value of
person in line 4: printing
8. Lines 4-7: The song is printed, with 'Andre' used as the value of
person in line 4: printing
9. End of line 10 after returning from the function call, and the
program is over.
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.
You can go back to having a main function again, and everything works.
Run birthday7.py :
def main():
happyBirthday('Emily')
happyBirthday('Andre')
main()
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 :
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 .)
happyBirthday(2)
as in example file birthdayBad.py , and then run it, you get something
close to:
[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.
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:
acts approximately as if the first lines executed inside the called function
f were
formal1 = actual1
formal2 = actual2
formal3 = actual3
f(x)=x2
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.
print(9)
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)
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 :
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.
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!
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()
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:
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.
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 :
def main():
x = 3
f(x)
def f(x):
print(x)
main()
def f(whatever):
print(whatever)
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()
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
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'
print(spanish['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:
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()
def main():
dictionary = createDictionary()
print('Count in Spanish: ' + dictionary['one'] + ', ' +
dictionary['two'] + ', ' +dictionary['three'] + ',
...')
print('Spanish colors: ' + dictionary['red'] + ', ' +
dictionary['blue'] + ', ' +dictionary['green'] + ',
...')
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.
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.)
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
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.
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:
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.
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:
© 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 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.
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.
list(range(4))
list(range(10))
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.
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.
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:
When executing step by step, note that the for loop heading serves two
purposes:
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.
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.
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:
(It would be even more like English if for were replace by for each , but
the shorter version is the one used by Python.)
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:
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.
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))
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.
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 :
1 red
2 orange
3 yellow
4 green
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.)
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.
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 :
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:
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:
The final value of number is never used, but that is OK. What we want
gets printed.
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).
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 :
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
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.
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
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
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:
This pattern will work in many other situations besides adding numbers.
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]
def joinStrings(stringList):
'''Join all the strings in stringList into one string,
and return the result, NOT printing it. For example:
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.
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:
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.
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):
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.
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
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.
Save it as numProduct.py and fix the error you found (and save again!).
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
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:
is equivalent to
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=' ')
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()
[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
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)
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.
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.
3**4
5*2**3
The result of a power operation is of int type only if both parameters are
integers and the correct result is an integer.
>>> 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.
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.'
>>> x = 2.876543
>>> 'longer: {x:.5f}, shorter: {x:.3f}.'.format(**locals())
'longer: 2.87654, shorter: 2.877.'
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:
(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
© 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
A. Python Shell
3. Escape codes inside literals include \' for a single quote and
\n for a newline. [Escape Codes]
a. stringExpression1 + stringExpression2
b. stringExpression * integerExpression
integerExpression * stringExpression
Example:
evaluates to:
H. Type list
dict()
3. Used in an expression,
dictName [ keyExpression ]
J. Type of None : This literal value has its own special type. None
indicates the absence of a regular object.
K. Identifiers
variable = expression
3. Return statement
return expression
N. Function calls
functionName ( expression , expression , and so on )
print( expression )
print( expression , expression , expression )
print( expression , expression , expression, sep= strVal,
end= strVal )
print()
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 )
6. range( expression )
for i in range( n ):
actions to be repeated
© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
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
s
s2 = s.upper()
s2
s
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.
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]
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:
There is a blank between the quotes in the line above. Blanks are
characters like any other (except you can’t see them)!
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:
3 * 'X'.count('XXX')
(3 * 'X').count('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
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 myData is a file , myData.read() will read and return the entire contents
of the file....
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
# 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
s[-1]
s[-3]
s[-10]
it = 'horse'
len(it)
it[-1]
it[1]
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 ]
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.
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[:]
word = 'program'
word[2:4]
word[1:-3]
word[3:]
word[3:3]
word[:1] + word[4:]
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 .
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
# 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)
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:
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.
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])
s .split()
s .split( sep )
2.1.6. join
For example (continuing in the Shell from the previous section, using
seq ), follow:
'##'.join(seq)
':'.join(['one', 'two', 'three'])
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.)
[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
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
'''
# 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?
newList = list() #2
for num in numList: #3
newList.append(num*multiplier) #4
return newList #5
Make sure the result makes sense to you or follow the details of playing
computer below.
« 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:
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.)
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
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?
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.
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
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
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:
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
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 :
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))
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}.
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
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.
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
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
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 .
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:
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:
Press return:
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.
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!
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:
def main():
win = GraphWin('Face', 200, 150) # give title and dimensions
win.yUp() # make right side up coordinates!
main()
Until further notice the set-off code is for you to read and have explained.
Immediately after the documentation string, always have the import line
in your graphics program, to allow easy access to the graphics.py
module.
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.
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.
The code above draws and displays a line, and illustrates another
method available to graphics object, setWidth , making a thicker line.
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....)
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.
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.
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.
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:
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)
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.
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.
p2 = win.getMouse()
p2.draw(win)
p3 = win.getMouse()
p3.draw(win)
Besides changing the style of a Text object, the text itself may be
changed:
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)
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.
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.
The version for this Tutorial has a few elaborations. Here is all their
documentation together:
or
'''Program: makeRectBad.py
Attempt a function makeRect (incorrectly),
which takes a takes a corner point and dimensions to construct a
Rectangle.
'''
def main():
win = GraphWin('Draw a Rectangle (NOT!)', 300, 300)
win.yUp()
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
Next, the assignment statement associates the name corner2 with the
same object. It is another name, or alias, for the original Point .
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()
corner2.move(width, height)
we get:
No conflict: corner and corner2 refer to the corners we want. Run the
corrected example program, makeRectangle.py .
Just for comparison, consider the corresponding diagrams for code with
int s that looks superficially similar:
a = 2
b = a
b = b + 3
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.
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:
def main():
win = GraphWin('Back and Forth', 300, 300)
win.yUp() # make right side up coordinates!
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.
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.
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')
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:
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).
Then in the main function the two similar animation loops are reduced to
a line for each direction:
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.
def main():
win = GraphWin('Back and Forth', 300, 300)
win.yUp() # make right side up coordinates!
win.promptClose(win.getWidth()/2, 20)
main()
«
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 :
Then, later, the animation uses the faceList to make the face go back
and forth:
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.
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 :
eye2End1 = eye1Center.clone()
eye2End1.move(15, 0)
eye2End2 = eye2End1.clone()
eye2End2.move(10, 0)
mouthCorner1 = center.clone()
mouthCorner1.move(-10, -10)
mouthCorner2 = mouthCorner1.clone()
mouthCorner2.move(20, -5)
def main():
win = GraphWin('Back and Forth', 300, 300)
win.yUp() # make right side up coordinates!
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()
then the head is easily drawn, using this center , rather than the previous
cir1 with its specific center point (40, 100):
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.
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:
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!
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 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)
def main():
win = GraphWin("Greeting", 300, 300)
win.yUp()
name = entry1.getText()
win.promptClose(instructions)
main()
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).
def main():
win = GraphWin("Addition", 300, 300)
win.yUp()
numStr1 = entry1.getText()
num1 = int(numStr1)
numStr2 = entry2.getText()
num2 = int(numStr2)
sum = num1 + num2
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.
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.
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:
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.
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.
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.
Such a creation can be used any place a color is used in the graphics,
(i.e. circle.setFill(aColor) ).
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)
win.promptClose(win.getWidth()/2, 20)
main()
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)
range(3, 40)
random.randrange(3, 40)
What are the smallest and largest values I allow for x and y? [3]
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.
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.
####
###
##
#
##
#
[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
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 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.
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:
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:
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.
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 .
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.
[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
stringReference [ intExpression ]
1. s .upper()
2. s .lower()
3. s .count( sub )
4. s .find( sub )
s .find( sub , start )
s .find( sub , start , end )
5. s .split()
s .split( sep )
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]
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' )
infile .read()
infile .close()
outfile .close()
7. Graphics
3. Event-driven programs
loop heading :
move all objects a small step in the proper
direction
time.sleep( delay )
© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
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
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.
if condition :
indentedStatementBlockForTrueCondition
else:
indentedStatementBlockForFalseCondition
These statement blocks can have any number of statements, and can
include about any kind of statement.
Math
Meaning Symbol Python Symbols
<
Less than <
>
Greater than >
Less than or equal ≤ <=
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]
'a' > 5
When the comparison does not make sense, an Exception is caused. [1]
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.
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 .
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.
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
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.
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.
Be sure to run your new version and test with different inputs that test all
the different paths through the program.
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,
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)
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.
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
if x < xLow:
dx = -dx
elif x > xHigh:
dx = -dx
if y < yLow:
dy = -dy
elif y > yHigh:
dy = -dy
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.)
'''
Show a ball bouncing off the sides of the window.
'''
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
bounceBall(3, 5)
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.
def printEven(nums):
'''Given a list of integers nums,
print the even ones.
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.
def chooseEven(nums):
'''Given a list of integers, nums,
return a list containing only the even ones.
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.
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.
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.
This is true if both credits >= 120 is true and GPA >= 2.0 is true. A short
example program using this would be:
if x < xLow:
dx = -dx
elif x > xHigh:
dx = -dx
condition1 or condition2
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
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:
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:
if end1 <= val <= end2 or end2 <= val <= end1:
return True
else:
return False
return end1 <= val <= end2 or end2 <= val <= end1
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:
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.
In general,
not condition
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.
pt1 = rect.getP1()
pt2 = rect.getP2()
return isBetween(point.getX(), pt1.getX(), pt2.getX()) and \
isBetween(point.getY(), pt1.getY(), pt2.getY())
corner2 = corner.clone()
corner2.move(width, -height)
rect = Rectangle(corner, corner2)
rect.setFill(color)
rect.draw(win)
return rect
def main():
win = GraphWin('pick Colors', 400, 400)
win.yUp() # right side up coordinates
if isInside(pt, redButton):
color = 'red'
elif isInside(pt, yellowButton):
color = 'yellow'
elif isInside(pt, blueButton):
color = 'blue'
else :
color = 'white'
house.setFill(color)
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 .
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 )
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?
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.
© 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
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
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:
Such tuples may be neatly handled in a for statement. You can imagine
a function to encapsulate the color choice starting:
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!
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:
As stated earlier, we could use the statements above and then make a
list of pairs with the statement
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.
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
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:
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.
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:
pt1 = rect.getP1()
pt2 = rect.getP2()
return isBetween(point.getX(), pt1.getX(), pt2.getX()) and \
isBetween(point.getY(), pt1.getY(), pt2.getY())
corner2 = corner.clone()
corner2.move(width, -height)
rect = Rectangle(corner, corner2)
rect.setFill(color)
rect.draw(win)
return rect
point = win.getMouse()
for (rectangle, choice) in choicePairs:
if isInside(point, rectangle):
return choice
return default
def main():
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))
#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.
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.
© 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
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:
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.
initialization
while continuationCondition :
do main action to be repeated
prepare variables for the next time through the loop
i = 4
while i < 9:
print(i)
i = i+2
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)
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
nums = list()
i = 4
while (i < 9):
nums.append(i)
i = i+2
print(nums)
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 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(10, 0, -1))
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:
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)
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:
')
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!
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,
lines = list()
print('Enter lines of text.')
print('Enter an empty line to quit.')
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 .
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!
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.
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 .
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
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!
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.
In this exercise you iterate the jump function for specific starting values n,
until the result is 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:
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.
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
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
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()
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:
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 ).
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.
for i in range(75):
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.
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). :
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 .
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.
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
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 bounceBall():
'''Make a ball bounce around the screen, and react to mouse
clicks.'''
#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
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.
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.
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.
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.
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!
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)
[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
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
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 = 'y'
if ans == 'y' or 'yes':
print('y is OK')
ans = 'no'
if ans == 'y' or 'yes':
print('no is OK!!???')
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
val = a or b
means
if bool(a):
val = a
else:
val = b
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.
© 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
Equals = ==
Not equal ≠ !=
value in sequence
2. condition1 or condition2
True only if at least one condition is True
3. not condition
True only when condition is False
5. if Statements
if condition :
indentedStatementBlockForTrueCondition
if condition :
indentedStatementBlockForTrueCondition
else:
indentedStatementBlockForFalseCondition
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.
while condition :
indentedStatementBlock
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
initialization
while continuationCondition :
main action to repeat
prepare variables for next time through loop
Often the code to input the first data and the later data is the
same, but it must appear in both places!
10. Graphics
© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.
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
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.)
© 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
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.
<h1>Web Introduction</h1>
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.
« 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.
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
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:
def main():
browseLocal(contents)
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,
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
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 main():
person = input('Enter a name: ')
contents = fileToStr('helloTemplate.html').format(**locals()) # NEW
browseLocal(contents)
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 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.
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....
© 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.
© 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
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,
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.
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
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("""
<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
htmlFormat = """
<html>
<Title>The Time Now</Title>
<body>
<p>The current Central date and time is: {timeStr}</p>
</body>
</html> """
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
# 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)
try: # NEW
print("Content-type: text/html\n\n") # say generating html
main()
except:
cgi.print_exception() # catch and print errors
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:
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.
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')
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.
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.
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 .
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.
Summary starts with the overall process for creating dynamic web pages.
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
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]
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]
form = cgi.FieldStorage()
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.
© Copyright 2012, Dr. Andrew N. Harrington. Last updated on Aug 08, 2013. Created using Sphinx 1.2b1.