You have probably used computers to do all sorts of useful and interesting things. In each application, the computer responds in different ways to your input, from the keyboard, mouse or a file. Still the underlying operations are determined by the design of the program you are given. In this set of tutorials you will learn to write your own computer programs, so you can give the computer instructions to react in the way you want.
First let us place Python programming in the context of the computer hardware. At the lowest level in the computer there are instructions built into the hardware. These are very simple instructions, peculiar to the hardware of your particular type of computer. The instructions are designed to be simple for the hardware to execute, not for humans to follow. The earliest programming was done with such instructions. If was difficult and error-prone. A major advance was the development of higher-level languages and translators for them. Higher-level languages allow computer programmers to write instructions in a format that is easier for humans to understand. For example
z = x+y
is an instruction in many high-level languages that means something like:
No computer understands the high-level instruction directly; it is not in machine language. A special program must first translate instructions like this one into machine language. This one high-level instruction might be translated into a sequence of three machine language instructions corresponding to the three step description above:
0000010010000001
0000000010000010
0000010110000011
Obviously high-level languages were a great advance in clarity!
If you follow a broad introduction to computing, you will learn more about the layers that connect low-level digital computer circuits to high-level languages.
There are many high-level languages. The language you will be learning is Python. Python is one of the easiest languages to learn and use, while at the same time being very powerful: It is used by many of the most highly productive professional programmers. A few of the places that use Python extensively are Google, the New York Stock Exchange, Industrial Light and Magic, .... Also Python is a free language! If you have your own computer, you can download it from the Internet....
If you are not sure whether your computer already has Python, continue to Section 1.2.2, and give it a try. If it works, you are all set.
If you do need a copy of Python, go to the Downloads page linked to http://www.python.org. Be careful to choose the version for your operating system and hardware.
You just need to execute the installer, and interact enough to agree to all the default choices. Python works in Windows as well as on Apples and in the free operating system Linux. The latest stable version is 2.5.
Double-click on the installer. Find and run the MacPython.mpkg that is inside. Follow the defaults for installation.
Python is generally installed, though Idle is not always installed. Look for something like 'idle-python' (the name in the Ubuntu distribution).
Although Python is a high-level language, it is not English or some other natural human language. The Python translator does not understand “add the numbers two and three”. Python is a formal language with its own specific rules and formats, which these tutorials will introduce gradually, at a pace intended for a beginner. These tutorials are also appropriate for beginners because they gradually introduce fundamental logical programming skills. Learning these skills will allow you to much more easily program in other languages besides Python. Some of the skills you will learn are
Guiding Principals for the Hands-on Python Tutorials:
Although this approach is an effective way to introduce material, it is not so good for reference. Referencing is addressed in several ways:
Some people learn better visually and verbally from the very beginning. Some parts of the tutorial will also have links to corresponding flash video segments. Many people will find reading faster and more effective, but the video segments may be particularly useful where a computer interface can be not only explained but actually demonstrated. The links to such segments will be labeled. They will need a broadband link or a CD (not yet generated).
In the Firefox browser, the incremental find is excellent, and particularly useful with the single web page version of the tutorials. (It only fails to search footnotes.) It is particularly easy to jump through different sections in a form like 1.2.4.
First you need to set up a location to store your work and the example programs from this tutorial. If you are on a Windows computer, follow just one of the three choices below to find an appropriate place to download the example archive examples.zip, and then follow the later instructions to unzip the archive.
In Windows, after you have chosen a location for the archive, examples.zip, download it by right clicking on http://cs.luc.edu/anh/python/hands-on/examples.zip and selecting “Save As” or the equivalent on your browser and then navigate to save the archive to the chosen location on your computer.
Once you have the archive, open a file browser window for that directory, 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.
Caution 1: On Windows, files in a zip archive can be viewed while they are still in the zip archive. Modifying and adding files is not so transparent. Be sure that you unzip the archive and work from the regular directory that holds the resulting unzipped files.
Caution 2: 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 webserver. In particular, that means you should not place your folder under “My Documents”. A directory like C:\hands-on or C:\python would be fine.
You also have the option of downloading
The disadvantage of a local copy is that the tutorial may be updated online after you get your download. The change log file http://www.cs.luc.edu/~anh/python/hands-on/changelog.html will show when the latest update was made and a summary of any major changes.
This section assumes Python is already on your computer. Windows does not come with Python. (To load Python see Section 1.1.2) On a Mac or Linux computer enough of Python comes installed to be able to run the sample program.
Before getting to the individual details of Python, you will run a simple text-based sample program. Find madlib.py in your Python folder (Section 1.2.1).
Options for running the program:
python madlib.py
The latter approach only works in a Windows command window if your operating system execution path is set up to find Python.
In whatever manner you start the program, run it, responding to the prompts on the screen. Be sure to press the enter key at the end of each response requested from you.
Try the program a second time and make different responses.
If you want to get right to the detailed explanations of writing your own Python, you can skip to the next section 1.2.4. If you would like an overview of a working program, even if all the explanations do not make total sense yet, read on.
Here is the text of the madlib.py program, followed by line-by-line explanations. The numbers on the right are not part of the program file. They are added for reference in the comments below.
""" 1
String Substitution for a Mad Lib 2
Adapted from code by Kirby Urner 3
""" 4
5
story = """ 6
Once upon a time, deep in an ancient jungle, 7
there lived a %(animal)s. This %(animal)s 8
liked to eat %(food)s, but the jungle had 9
very little %(food)s to offer. One day, an 10
explorer found the %(animal)s and discovered 11
it liked %(food)s. The explorer took the 12
%(animal)s back to %(city)s, where it could 13
eat as much %(food)s as it wanted. However, 14
the %(animal)s became homesick, so the 15
explorer brought it back to the jungle, 16
leaving a large supply of %(food)s. 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
print story % userPicks 27
28
def addPick(cue, dictionary): 29
prompt = "Enter a specific example for %s: " % cue 30
dictionary[cue] = raw_input(prompt) 31
32
tellStory() 33
raw_input("Press Enter to end the program.") 34
Line By Line Explanation
""" 1
String Substitution for a Mad Lib 2
Adapted from code by Kirby Urner 3
""" 4
There is multi-line text enclosed in triple quotes. Quoted text is called a string. A string at the very beginning of a file like this is documentation for the file.
Blank lines are included for human readability to separate logical parts. The computer ignores the blank lines.
story = """ 6
Once upon a time, deep in an ancient jungle, 7
there lived a %(animal)s. This %(animal)s 8
liked to eat %(food)s, but the jungle had 9
very little %(food)s to offer. One day, an 10
explorer found the %(animal)s and discovered 11
it liked %(food)s. The explorer took the 12
%(animal)s back to %(city)s, where it could 13
eat as much %(food)s as it wanted. However, 14
the %(animal)s became homesick, so the 15
explorer brought it back to the jungle, 16
leaving a large supply of %(food)s. 17
18
The End 19
""" 20
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, story.
These lines contain the body of the string and the ending triple quotes. This story string is different from the documentation string of lines 1-4. It has a special form, called a format string. It will be used later to provide a format into which substitutions are made. The parts of the string starting with % and ending with s 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 parentheses: (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 %(...)s is currently.
def tellStory(): 22
userPicks = dict() 23
addPick('animal', userPicks) 24
addPick('food', userPicks) 25
addPick('city', userPicks) 26
print story % userPicks 27
def is short for def inition. This line is the heading of a def inition, which makes the name tellStory becomes def ined as a short way to refer to the sequence of statements that start indented on line 23, and continue through line 27.
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().
addPick is the name for a sequence of instructions defined on lines 29-31 for adding another definition to a dictionary, based on the user's input. The result of these three lines is to add definitions for each of the three words 'animal', 'food', and 'city' to the dictionary called userPicks.
This is where all the work becomes visible: Print the story format string with substitutions from the dictionary userPicks, to give the user's customized story. The '%' here is an operator on the string story that essentially means 'with substitutions from'.
def addPick(cue, dictionary): 29
prompt = "Enter a specific example for %s: " % cue 30
dictionary[cue] = raw_input(prompt) 31
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 30 and line 31. 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.
On the right side of the equal sign is an expression involving a format string, much simpler than story. The resulting string just has the current value of cue substituted for the %s. This resulting string is then given the name prompt.
The right-hand-side of this equal sign causes an interaction with the user. 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. The left-hand-side of the equal sign is a reference to the definition of the cue word in the dictionary. The whole line ends up making the definition of the current cue word become the string typed by the user.
tellStory() 33
raw_input("Press Enter to end the program.") 34
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.
This line is only here to accommodate running the program in Windows by double clicking on its file icon. Without this line, the story would be displayed and then the program would end, and Windows would make it immediately disappear from the screen! This line forces the program to continue being displayed until there is another response from the user, and meanwhile the user may look at the output from tellStory.
The program that translates Python instructions and then executes them is the Python interpreter.
This interpreter is embedded in a number of larger programs that make it particularly easy to develop Python programs. Such a programming environment is Idle, and it is a part of the standard distribution of Python.
Read the section that follows for your operating system:
(Assuming you already have Python installed.) Display your Python folder. You should see icon for Idle25Shortcut (and maybe a similar icon with a number different than 25 - ignore any other unless you know you are using that version of Python). Double click on the appropriate shortcut, and an Idle window should appear. After this the instructions are the same in any operating environment. It is important to start Idle through these in several circumstances. It is best if it you make it a habit to use this shortcut. For example the alternative of opening an existing Python program in Windows XP or Vista from Open With Idle in the context menu looks like it works at first but then fails miserably but inexplicably when you try to run a graphics program.
the new version of Python and Idle should be in a folder called MacPython 2.5, inside the Applications folder. It is best if you can open a terminal window, change into your Python folder from Section 1.2.1, and enter the command
idle
If the command is not recognized, you may need to include the full path to the idle program.
The approach depends on the installation. In Ubuntu, you should fine idle in the Programming section of the Applications menu. As with OS X above, you are better starting idle from a terminal, with the current directory being your Python folder.
Idle has several parts you may choose to display, each with its own window. Depending on the configuration, Idle can start up showing either of two windows, an Edit Window or a Python Shell Window. You are likely to first see an Edit window, whose top left corner looks something like in Windows:
For more on the Edit Window, see Section1.9.
If you see this Edit Window with its Run menu on top, go to the Run menu and choose PYTHON SHELL to open a Python Shell Window for now. Then you may close the Edit Window.
Either initially, or after explicitly opening it, you should now see the Python Shell window, with a menu like the following, though the text may be slightly different:
Look at the Python Shell. ...
In the Shell the last line should look like
>>>
The >>> is the prompt, telling you Idle is waiting for you to type something. Continuing on the same line enter
6+3
Be sure to end with the Enter key. After the Shell responds, you should see something like
>>> 6+3
9
>>>
The shell evaluates the line you entered, and prints the result. You see Python does arithmetic. At the end you see a further prompt >>> where you can enter your next line.... The result line, showing 9, that is produced by the computer, does not start with “> > >”.
Python directly recognizes a variety of types of data. Here are a few:
3, 6, -7, 1.25
'hello', 'The answer is: '
[1, 2, 3, 4], ['yes', 'no', 'maybe']
None
Python has large collection of built-in functions that operate on different kinds of data to produce all kinds of results. To make a function do its action, parentheses are required. These parentheses surround the parameter or parameters, as in a function in algebra class.
The general syntax to execute a function is
functionName ( parameters )
One function is called type, and it returns the type of any object. The Python Shell will evaluate functions. In the Shell the last line should look like
>>>
Continuing on the same line enter
type(7)
Always remember to end with the Enter key. After the Shell responds, you should see something like
>>> type(7)
<type 'int'>
>>>
In the result, int is short for integer. 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 Section 1.14.1. Enter
type('hello')
In your last result you see another abbreviation: str rather than string. Enter
type([1, 2, 3])
Strings and lists are both sequences of parts (characters or elements). We can find the length of that sequence with another function with the abbreviated name len. Try both of the following, separately, in the Shell:
len([2, 4, 6])
len('abcd')
Some functions have no parameters, so nothing goes between the parentheses. For example, some types serve as no-parameter functions to create a simple value of their type. Try
list()
You see the way an empty list is displayed.
Functions may also take more than one parameter. Try
max(5, 11, 2)
Above, max is short for maximum.
Some of the names of types serve as conversion functions (where there is an obvious meaning for the conversion). Try each of the following, one at a time, in the Shell:
str(23)
int('125')
An often handy Shell feature: an earlier Shell line may to copied and edited by clicking anywhere in the previously displayed line and then pressing Enter. 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.
We start with the integers and integer arithmetic, not because arithmetic is exciting, but because the symbolism should be mostly familiar. Of course arithmetic is important in many cases, but Python is probably more often used to manipulate text and other sorts of data, as in the sample program in Section 1.2.2.
Python understands numbers and standard arithmetic. For the whole section on integer arithmetic, where you see a set-off line in typewriter font, type individual lines at the > > > prompt in the Python Shell. Press Enter after each line to get Python to respond:
77
2 + 3
5 - 7
Python should evaluate and print back the value of each expression. Of course the first one does not require any calculation. It appears the shell just echoes back what you printed. Do 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.
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.
Try in the Shell:
2 x 3
You should get your first syntax error. The 'x' should have become highlighted, indicating the location where the Python interpreter discovered that it cannot understand you: Python does not use x for multiplication as you may have done in grade school. The x can be confused with the use of x as a variable (more on that later). Instead the symbol for multiplication is an asterisk '*'. Enter each of the following. You may include spaces or not. The Python interpreter can figure out what you mean either way. Try in the Shell:
2*5
2 + 3 * 4
If you expected the last answer to be 20, think again: Python uses the normal precedence of arithmetic operations: Multiplications and divisions are done before addition and subtraction, unless there are parentheses. Try
(2+3)*4
2 * (4 - 1)
Now try the following in the Shell, exactly as written followed by Enter, with no closing parenthesis:
5 * (2 + 3
Look carefully. There is no answer given at the left margin of the next line and no prompt > > > to start a new expression. If you are using Idle, the cursor has gone to the next line and has only indented slightly. Python is waiting for you to finish your expression. It is smart enough to know that opening parentheses are always followed by the same number of closing parentheses. The cursor is on a continuation line. Type just the matching close-parenthesis and Enter,
)
and you should finally see the expression evaluated. (In some versions of the Python interpreter, the interpreter puts '...' at the beginning of a continuation line, rather than just indenting.)
Negation also works. Try in the Shell:
-(2 + 3)
If you think about it, you learned several ways to do division. In the earliest grades you would say “14 divided by 4 is 3 with a remainder of 2”. The problem here is that the answer is in two parts, the integer quotient 3 and the remainder 2. Python has separate operations to generate each part. Python uses the common division symbol / for the operation that produces just the quotient, and introduces the symbol % for the operation of finding the remainder. Try each in the Shell
14/4
14%4
Now predict and then try each of
23/5
23%5
20%5
6/8
6%8
Finding remainders will prove more useful than you might think in the future.
Of course as you moved on in school, you learned that 6/8 is also 0.75. To get that sort of result in Python, you need decimal notation, which brings in extra complications in Section 1.14.1
Enough with numbers for a while. Strings of characters are another important type in Python.
A string in Python is a sequence of characters. For Python to recognize a sequence of characters, like hello, as a string, it must be enclosed in quotes to delimit the string.
For this whole section on strings, continue trying each set-off line of code in the Shell. Try
"hello"
Note that the interpreter gives back the string with single quotes. Python does not care what system you use. Try
'Hi!'
Having the choice of delimiters can be handy.
Exercise 1.5.1.1. * Figure out how to give Python the string containing the text: I'm happy. Try it. If you got an error, try it with another type of quotes, and figure out why that one works and not the first.
There are many variations on delimiting strings and embedding special symbols. We will consider more ways later in Section 1.8.
A string can have any number of characters in it, including 0. The empty string is '' (two quote characters with nothing between them).
Strings are a new Python type. Try
type('dog')
type('7')
type(7)
The last two lines show how easily you can get confused! Strings can include any characters, including digits. Quotes turn even digits into strings. This will have consequences in the next section....
Strings also have operation symbols. Try in the Shell (noting the space after very):
'very ' + 'hot'
The plus operation with strings means concatenate the strings.
Think of the relation of addition and multiplication of integers, and then guess the meaning of
3*'very ' + 'hot'
Were you right? The ability to repeat yourself easily can be handy.
Exercise 1.5.2.1. * Figure out a compact way to get Python to make the string, “YesYesYesYesYes”, and try it. How about “MaybeMaybeMaybeYesYesYesYesYes” ? Hint: 1
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! 2
Each set-off line in this section should be tried in the Shell.
Try
width = 10
Nothing is displayed by the interpreter after this entry, so it is not clear anything 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 statement associates a variable name on the left of the equal sign with the value of an expression calculated from the right of the equal sign. Enter
width
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 entered.
The interpreter does not print a value after an assignment statement because the value of the expression on the right is not lost. It can be recovered if you like, by entering the variable name and we did above.
Try each of the following lines:
height = 12
area = width * height
area
The equal sign is an unfortunate choice of symbol for assignment, since Python's usage is not the mathematical usage of the equal sign. If the symbol had appeared on keyboards in the early 1990's, it would probably have been used for assignment instead of =, emphasizing the asymmetry of assignment. In mathematics an equation is an assertion that both sides of the equal sign are already, in fact, equal. A Python assignment statement forces the variable on the left hand side to become associated with the value of the expression on the right side. The difference from the mathematical usage can be illustrated. Try:
10 = width
so this is not equivalent in Python to width = 10. The left hand side must be a variable, to which the assignment is made. Try
width = width + 5
This is, of course, nonsensical as mathematics, but it makes perfectly good sense as an assignment. Can you figure out the value that is now associated with width? Check by entering
width
In the assignment statement, the expression on the right is evaluated first. At that point width was associated with its original value 10, so width + 5 had the value of 10 + 5 which is 15. That value was then assigned to the variable on the left (width again) to give it a new value. We will modify the value of variables in a similar way routinely.
Assignment and variables work equally well with strings. Try:
first = 'Sue'
last = 'Wong'
name = first + ' ' + last
name
Try entering:
first = fred
Note the different form of the error message. The earlier errors in these tutorials were syntax errors: errors in translation of the instruction. In this last case the syntax was legal, so the interpreter went on to execute the instruction. Only then did it find the error described. There are no quotes around fred, so the interpreter assumed fred was an identifier, but the name fred was not defined at the time of execution.
It is easy to forget quotes where you need them and put them around a variable name that should not have them!
Try in the Shell:
fred = 'Frederick'
first = fred
first
Now fred, without the quotes, makes sense.
There are more subtleties to assignment and the idea of a variable being a “name for” a value, but we will worry about them later, in Section 2.4.6. They do not come up if our variables are just numbers and strings.
Autocompletion: A handy short cut. Python 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-/. You should see f autocompleted to be first. This is particularly useful if you have long identifiers! You can press Alt-/ several times if more than one identifier starts with the initial sequence of characters you typed. If you press Alt-/ again you should see fred. Backspace and edit so you have fi, and then and press Alt-/ again. You should not see fred this time, since it does not start with fi.
Expressions like 27 or 'hello' are called literals, coming from the fact that they literally mean exactly what they say. They are distinguished from variables, who value is not directly determined by their name.
The sequence of characters used to form a variable name (and names for other Python entities later) is called an identifier. It identifies a Python variable or other entity.
There are some restrictions on the character sequence that make up an identifier:
and del for is raise
assert elif from lambda return
break else global not try
class except if or while
continue exec import pass yield
def finally in print
There are also identifiers that are automatically defined in Python, and 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.
What is legal is distinct from what is conventional or good practice or recommended. Meaningful names for variables are important for the humans who are looking at programs, understanding them, and revising them. That sometimes means you would like to use a name that is more than one word long, like price at opening, but blanks are illegal! One poor option is just leaving out the blanks, like priceatopening. Then it may be hard to figure out where words split. Two practical options are
Use the choice that fits your taste (or the taste of the people you are working with).
In interactive use of the Python interpreter, you can type an expression and immediately see the result of its evaluation. This is fine to test out syntax and maybe do simple calculator calculations. In a program run from a file like the first sample program, Python does not display expressions this way. If you want your program to display something, you can give explicit instructions with a print statement. Try in the Shell:
x = 3
y = 5
print 'The sum of', x, 'plus', y, 'is', x+y
The print statement will prints as strings everything in a comma-separated sequence of expressions, and it will separate the results with single blanks. Note that you can mix types: anything that is not already a string is automatically converted to its string representation.
There are several other variations. Try these two in the Shell:
print 'Hi!'
print
You can print a single expression or no expressions. Each of the versions above advances to a new line. The last version with no parameters only advances to the next line. This is one way to leave a blank line in your output.
Strings delimited by one quote character are required to lie within a single Python line. It is sometimes convenient to have a multi-line string, which can be delimited with triple quotes: Try typing the following. You will get continuation lines until the closing triple quotes. Try in the Shell:
sillyTest = '''Say,
"I'm in!"
This is line 3'''
print sillyTest
The line structure is preserved in a multi-line string. As you can see, this also allows you to embed both single and double quote characters!
Continuing in the Shell with sillyTest, enter just
sillyTest
The answer looks strange! It indicates an alternate way to encode the string using escape codes. Escape codes are embedded inside string literals and start with a backslash character (\). They are used to embed characters that are either unprintable or have a special syntactic meaning to Python that you want to suppress. In this example you see the most common ones:
Escape code | Meaning |
\' | ' |
\n | newline |
\\ | \ |
The newline character indicates further text should appear on a new line. When you use a print statement, you get the actual printed meaning of the escaped coded character.
Predict the result, and try in the Shell:
print 'a\nb\n\nc'
Did you guess the right number of lines splitting in the right places?
It is time to put longer collections of instructions together. That is most easily done by creating a text file and running the Python interpreter on the file. Idle simplifies that process.
First you can put an existing file into an Idle Edit Window. Click on the Idle File menu and select Open. (Or as you see, you can use the shortcut Ctrl+O. That means holding down the Ctrl key, and pressing the letter O for Open.) You should get a file selection dialog. You should have the sample program madlib.py displayed in the list. Select it and open it. (If you do not see the program, then you either failed to download the example programs, Section 1.2.1, or you did not start Idle in the proper folder, Section 1.2.4.)
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 and print are likely orange. Look at the last two lines, where the identifier tellStory is black, and the identifier raw_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.
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 2.5.1. 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.
The problem only comes up because you interrupted the last execution when user input was being waited for. The restart was not complete here: The system is still looking for the pending user input from the last execution.
The fix is simple: Make sure the Interpreter Window is the currently selected window, and press return to terminate the lost user input. In some circumstances, you may need to press return a second time.
After that the program should start up normally with its prompt.
Watch out for this behavior, and remember the fix.
Make sure you have Idle started in your Python directory (in Windows with the provided Idle shortcut link), where you will store program files. (Do not start Idle from the Windows Start Menu!) If you just started Idle now, you may already have a blank Edit Window in front of you. If not, open a new window by going to the File menu and selecting New Window. This gives you a rather conventional text editing window with the mouse available, ability to cut and paste, plus a few special options for Python.
Type (or paste) the following into the editor window:
print 'Hello world!'
Save the file with the File menu -> 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 .
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. (In version 2.4 of Python, the coloring only happens after you save your file with the '.py' ending.)
Now that you have a complete, saved program, choose Run menu -> Run Module. You should see the program run in the Python Shell window.
You just wrote and executed a program. Unlike when you use the shell, this code is saved to a file in your Python folder. You can open and execute the file any time you want. (In Idle, use File->Open.)
To the interpreter, a program source file corresponds to a Python module. We will tend to use the more general term: a program file is a module. Note the term from the menu when running the program.
Distinguish program code from Shell text: It is easy to confuse the Shell and the Edit windows. Make sure you keep them straight. The hello.py program is just the line
print 'Hello world!'
that you typed into the edit window and saved. When you ran the program in Idle, you saw results in the Shell. First came the Restart notice, the one-line output from the program saying hello, and a further Shell prompt:
>>> ================================ RESTART ========
>>>
Hello world!
>>>
You could also have run this single print statement directly in the Shell in response to a Shell prompt. When you see >>>, you could enter the print statement and get the exchange between you and the Shell:
>>> print 'Hello world'
Hello world!
>>>
The three lines above are not a program you could save in a file and run. This is just an exchange in the Shell, with its >>> prompts, individual line to execute and the response. Again, just the single line statement, with no >>>,
print 'Hello world!'
entered into the Edit window forms a program you can save and run. We will shortly get to more interesting many-statement programs, where it is much more convenient to use the Edit window than the Shell!
The program above is self evident, and shows how short and direct a program can be (unlike other languages like Java). Still, right away, get used to documenting a program. Python has a special feature: If the beginning of a program is just a quoted string, that string is taken to be the program's documentation string. Open the example file hello2.py in the Edit window:
'''A very simple program,
showing how short a Python program can be!
Authors: ___, ___
'''
print 'Hello world!' #This is a stupid comment after the # mark
Most commonly, the initial documentation goes on for several lines, so a multi-line string delimiter is used (the triple quotes). Just for completeness of illustration in this program, another form of comment is also shown, a comment that starts with the symbol # and extends to the end of the line. The Python interpreter completely ignores this form of comment. Such a comment should only be included for better human understanding. Avoid making comments that do not really aid human understanding. (Do what I say, not what I did above.) Good introductory comment strings and appropriate names for the parts of your programs make the # symbol comments less needed.
Run the program and see the documentation and comment make no difference in the result.
Of course you can arrange the windows on your computer screen any way that you like. A suggestion as you start to use the combination of the editor to write, the shell to run, and the tutorial to follow along: Make all three mostly visible your computer screen at once. Drag the editor window to the upper left. Place the Shell window to the lower left, and perhaps reduce its height a bit so there is not much overlap. If you are looking at the web version of the tutorial on the screen, make it go top to bottom on the right, but not overlap the Idle windows too much. The web page rendering should generally adapt to the width pretty well. You can always temporarily maximize the window. Before resizing the browser window, it is good to look for an unusual phrase on your page, and search for it after resizing, since resizing can totally mess up your location in the web page.
There is an alternative to maximization for the Idle editor window: It you want it to go top to bottom of the screen but not widen, you can toggle that state with Alt-2. Play with all this.
The hello program of Section 1.9.3 always does the same thing. This is not very interesting. Programs are only going to be reused if they can act on a variety of data. One way to get data is directly from the user. Modify the hello.py program as follows in the editor, and save it from the File menu with Save As...., using the name hello_you.py.
person = raw_input('Enter your name: ')
print 'Hello', person
Run the program. In the Shell you should see
Enter your name:
Follow the instruction (and press Enter). Make sure the typing cursor is in the Shell window, at the end of this line. After you type your response, you can see that the program has taken in the line you typed. That is what the built-in function raw_input does: First it prints the string you give as a parameter (in this case 'Enter your name: '), and then it waits for a line to be typed in, and returns the string of characters you typed. In the hello_you.py program this value is assigned to the variable person, for use later.
The parameter inside the parentheses after raw_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 user would not know what was happening, and the computer would just sit there waiting!
Open the example program, interview.py. Before running it (with any made-up data), see if you can figure out what it will do:
'''Illustrate raw_input and print.'''
applicant = raw_input("Enter the applicant's name: ")
interviewer = raw_input("Enter the interviewer's name: ")
time = raw_input("Enter the appointment time: ")
print interviewer, "will interview", applicant, "at", time
The statements are executed in the order they appear in the text of the program: sequentially. This is the simplest way for the execution of the program to flow. You will see instructions later that alter that natural flow.
If we want to reload and modify the hello_you.py program to put an exclamation point at the end, you could try:
person = raw_input('Enter your name: ')
print 'Hello', person, '!'
Run it and you see that it is not spaced right. There should be no space after the person's name, but each field in a print statement is always 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: 3
One way to put punctuation after the person in hello_you.py is to use the plus operator. While we are at it, lets turn everything into one field. Note the space inside the end of the 'Hello ' string. Save as hello_you2.py:
person = raw_input('Enter your name: ')
print 'Hello ' + person + '!'
It is common to want to mix preset strings, like 'Hello ' and '!' interspersed with the values of one or more variables or expressions, like person.
One way to think of this is “fill in the blanks”, with a format like:
Hello _____!
where there are parts of the desired string that are fixed ('Hello ' and '!') and one or more places where
substitutions are to be made (in the 'blank'). If we want to ”'fill in the blank” with the value of the
variable person, which might refer to 'Sue', then the final string after filling in the blank would be
'Hello Sue!'.
Instead of a long underscore to indicate the “blank” in the example above, Python uses the special symbolism '%s' in the place where a substitution is to be made, so the format string in this case would be 'Hello %s!'. Filling in the blanks in a format string is an operation in Python with the operator % so the general syntax is:4
format_string % substitutions
The simplest way to illustrate this is in the Shell: First give person a value by typing into the Shell
person = 'Sue'
Then enter the format expression in the Shell:
'Hello %s!' % person
Now back to your hello_you2.py program. Add a line to print out the expression above:
person = raw_input('Enter your name: ')
print 'Hello ' + person + '!'
print 'Hello %s!' % person
then run it with any name and see that the last two lines do the same thing
This syntax is not too impressive in this situation with one substitution, but in fact many substitutions may be made. Consider the interview program. Suppose we want to add a period at the end of the sentence. One approach would be to combine everything with plus signs. Another way is with the format operator. Here the idea is to fill in the blanks in
_____ will interview _____ at _____.
Can you figure out what the Python syntax for the format string will be?
Run the example file interview2.py, and check that the results combining parts with the + operator match the result with the format string:
'''Compare print with concatenation and with format string.'''
applicant = raw_input("Enter the applicant's name: ")
interviewer = raw_input("Enter the interviewer's name: ")
time = raw_input("Enter the appointment time: ")
print interviewer + ' will interview ' + applicant + ' at ' + time +'.'
print '%s will interview %s at %s.' % (interviewer, applicant, time)
Each %s in the format string indicates a substitution, so there are three in all. The syntax with the format operator % after the format string allows just one further operand, while we have three substitutions to make. To convert the three values into a single object, they are put into a single tuple of values: a parentheses enclosed, comma separated sequence, (interviewer, applicant, time) in this case. The format operator substitutes values in the same order as they appear in the tuple. Always make sure your tuple includes the exact number of values required by the format string. A tuple is more human readable with space after the commas, but it does not matter to the interpreter.
Format strings with this sort of format match up with parameter values in order. There is another approach with a dictionary, that was used in the first sample program, and will be discussed more in Section 1.12.2 on dictionaries.
Predict the results of the example file lunch.py shown below,and then check yourself by running it:
'''Fancier format string example.'''
part1 = 'lunch'
part2 = 'box'
format = 'The words %s and %s can appear separately or together: %s%s.'
print format % (part1, part2, part1, part2)
Exercise 1.10.2.1. * Create a modified program lunch2.py, which extends the formatted print statement to print not only lunchbox, but also boxlunch. Hint: 5
In section 1.14.3 you will see another of the many options for format string substitutions. All the special codes for substitutions start with a '%'. This raises the question of what to do when you really want a '%' in the final formatted string! A final single '%' is generated by the special code '%%' in the format string. For instance the code
x = 90
print 'The result is %s%%.' % x
prints
The result is 90%.
Consider the following problem: Prompt the user for two numbers, and then print out a sentence stating the sum. For instance if the user entered 2 and 3, you would print “The sum of 2 and 3 is 5.”
You might imagine a solution like the example file addition1.py, shown below. There is a problem. Can you figure it out before you try it? Hint: 6 End up running it in any case.
x = raw_input("Enter a number: ")
y = raw_input("Enter a second number: ")
print 'The sum of %s and %s is %s.' % (x, y, x+y) # error!
We do not want string concatenation, but integer addition. We need integer operands. Briefly mentioned in Section 1.3 was the fact that we can use type names as functions to convert types. One approach would be to do that. Further variable names are also introduced in the example addition2.py file below to emphasize the distinctions in types:
'''Conversion of strings to int before addition'''
xString = raw_input("Enter a number: ")
x = int(xString)
yString = raw_input("Enter a second number: ")
y = int(yString)
print 'The sum of %s and %s is %s.' % (x, y, x+y)
This calculates x+y correctly, but you might wonder why it prints correctly, since the format specifiers, %s, are for strings, when we have just taken pains to generate integers! In fact, any type of object can be printed as some form of a string, and Python will convert automatically for a %s in a format string.
Run it to check.
We will see later in web pages and files that we will be given string versions of integers and want to convert to actual integers for arithmetic, so the idea above is important. On the other hand, in the situation of getting values directly from the keyboard, there is a simpler alternative....
You noticed the modifier “raw” in raw_input. There is also a version called input that processes the input as if you typed it into the shell as an expression to be evaluated, recognizing a number when it sees it, and translating and evaluating automatically. Try the variation in example file, addition3.py:
'''Two numeric inputs'''
x = input("Enter a number: ")
y = input("Enter a second number: ")
print 'The sum of %s and %s is %s.' % (x, y, x+y)
The prompt works the same way as with raw_input. This version is much more powerful than the version with the int conversion of strings. As in the shell, arbitrary expressions in the input are evaluated:
As a test, run the program entering the expressions 5-1 and 2*3.
See the code below. We introduced tuples for the string format operation. In many situations, including this situation with keyboard input, it is the commas that force the formation of a tuple. We could ask for two numbers to be entered together, comma separated, and the input function returns a tuple. The first statement below is written with the assumption that a tuple will be entered, because there is also a comma on the left-hand side of the assignment, creating a multiple assignment statement. The variables on the left associate with the values of the tuple parts on the right in order. Check out and run the example file addition4.py:
'''Two numeric inputs in one input statement'''
x, y = input("Enter two comma separated numbers: ")
print 'The sum of %s and %s is %s.' % (x, y, x+y)
Run it. If you enter the following at the prompt:
2, 3
the code should work. Then x gets the value 2 and y gets the value 3.
Exercise 1.10.4.1. * Write a version, add3.py, that asks for three numbers, and lists all three, and their sum, in similar format to the example above.You may use tuples or not, as you like (or try both).
Exercise 1.10.4.2. * Write a program, quotient.py, that prompts the user for two integers, and then prints them out in a sentence with an integer division problem like "The quotient of 14 and 3 is 4 with a remainder of 2". Review Section 1.4.3 if you forget the integer division or remainder operator.
The simple programs so far have followed a basic programming pattern: input-calculate-output. Get all the data first, calculate with it second, and output the results last. The pattern sequence would be even clearer if we explicitly create a named result variable in the middle:
x, y = input("Enter two comma separated numbers: ")
sum = x + y
print 'The sum of %s and %s is %s.' % (x, y, sum)
We will see more complicated patterns involving repetition in the future.
When new Python syntax is introduced, the usual approach will be to give both specific examples and general templates. In general templates for Python syntax the typeface indicates the the category of each part:
Typeface | Meaning |
Typewriter font | Text to be written verbatim |
Emphasized | A place where you can use an arbitrary identifier. The emphasized text attempts to be descriptive of the meaning of the identifier in the current context. |
Normal text | A description of what goes in that position, without giving explicit syntax |
We will use these conventions shortly in the discussion of function syntax, and will continue to use the conventions throughout the tutorial.
If you know it is the birthday of a friend, Emily, you might tell those gathered with you to sing "Happy Birthday to Emily".
We can make Python display the song. Read, and run if you like, the example program birthday1.py:
print "Happy Birthday to you!"
print "Happy Birthday to you!"
print "Happy Birthday, dear Emily."
print "Happy Birthday to you!"
You would probably not repeat the whole song to let others know what to sing. You would give a request to sing via a descriptive name like "Happy Birthday to Emily".
In Python we can also give a name like happyBirthdayEmily, and associate the name with whole song by using a function definition. We use the Python def keyword, short for define.
Read for now:
def happyBirthdayEmily():
print "Happy Birthday to you!"
print "Happy Birthday to you!"
print "Happy Birthday, dear Emily."
print "Happy Birthday to you!"
There are several parts of the syntax for a function definition to notice:
The heading contains def, the name of the function, parentheses, and finally a colon.
def function_name():
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. This is important: the statements in the function definition are not executed as Python first passes over the lines.
The code above is in example file birthday2.py. Load it in Idle and execute it from there. Nothing should happen visibly. This is just like defining a variable: Python just remembers the function definition for future reference. After Idle finished executing a program, however, its version of the Shell remembers function definitions from the program.
In the Idle Shell (not the editor), enter
happyBirthdayEmily
The result probably surprises you! When you give the Shell an identifier, it tells you its value. Above, without parentheses, it identifies the function code as the value (and gives a location in memory of the code). Now try the name in the Idle Shell with parentheses added:
happyBirthdayEmily()
The parentheses tell Python to execute the named function rather than just refer to the function. Python goes back and looks up the definition, and only then, executes the code inside the function definition. The term for this action is a function call or function invocation. Note, in the function call there is no def, but there is the function name followed by parentheses.
function_name()
In many cases we will use a feature of program execution in Idle: that after program execution is completed, the Idle Shell still remembers functions defined in the program. This is not true if you run a program by selecting it directly in the operating system. 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. (With most of the examples in the tutorial, running from the operating system is OK – the execution method will not actually matter.)
Look at the example program birthday3.py. See it just adds two more lines, not indented. Can you guess what it does? Try it:
def happyBirthdayEmily(): #1
print "Happy Birthday to you!" #2
print "Happy Birthday to you!" #3
print "Happy Birthday, dear Emily." #4
print "Happy Birthday to you!" #5
happyBirthdayEmily() #6
happyBirthdayEmily() #7
The execution sequence is different from the textual sequence:
Functions alter execution order in several ways: statements not being executed as the definition is first read, and then when the function is called during execution, jumping to the function code, and back at the the end of the function execution.
If it also happens to be Andre's birthday, we might define a function happyBirthdayAndre, too. Think how to do that before going on ....
Here is example program birthday4.py where we add a function happyBirthdayAndre, and call them both. Guess what happens, and then try it:
def happyBirthdayEmily(): # same old function
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 happyBirthdayEmily(): #1
print "Happy Birthday to you!" #2
print "Happy Birthday to you!" #3
print "Happy Birthday, dear Emily." #4
print "Happy Birthday to you!" #5
def happyBirthdayAndre(): #6
print "Happy Birthday to you!" #7
print "Happy Birthday to you!" #8
print "Happy Birthday, dear Andre." #9
print "Happy Birthday to you!" #10
def main(): #11
happyBirthdayAndre() #12
happyBirthdayEmily() #13
main() #14
If we want the program to do anything automatically when it is runs, we need one line outside of definitions! The final line is the only one directly executed, and it calls the code in main, which in turn calls the code in the other two functions.
Detailed order of execution:
There is one practical difference from the previous version. After execution, if we want to give another round of Happy Birthday to both persons, we only need to enter one further call in the Shell to:
main()
As a simple example emphasizing the significance of a line being indented, guess what the the example file order.py does, and run it to check:
def f():
print 'In function f'
print 'When does this print?'
f()
Modify the file so the second print statement 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.
Exercise 1.11.3.1. * Write a program, poem.py, that defines a function that prints a short poem or song verse. Give a meaningful name to the function. Have the program end by calling the function three times, so the poem or verse is repeated three times.
As a young child, you probably heard Happy Birthday sung to a couple of people, and then you could sing to a new person, say Maria, without needing to hear the whole special version with Maria's name in it word for word. You had the power of abstraction. With examples like the versions for Emily and Andre, you could figure out what change to make it so the song could be sung to Maria!
Unfortunately, Python is not that smart. It needs explicit rules. If you needed to explain explicitly to someone how Happy Birthday worked in general, rather than just by example, you might say something like this:
First you have to be given a person's name. Then you sing the song with the person's name inserted at the end of the third line.
Python works something like that, but with its own syntax. The term “person's name” serves as a stand-in for the actual data that will be used, “Emily”, “Andre”, or “Maria”. This is just like the association with a variable name in Python. “person's name” is not a legal Python identifier, so we will use just person as this stand-in.
The function definition indicates that the variable name person will be used inside the function by inserting it between the parentheses of the definition. Then in the body of the definition of the function, person is used in place of the real data for any specific person's name. Read and then run example program birthday6.py:
def happyBirthday(person): #1
print "Happy Birthday to you!" #2
print "Happy Birthday to you!" #3
print "Happy Birthday, dear " + person + "." #4
print "Happy Birthday to you!" #5
happyBirthday('Emily') #6
happyBirthday('Andre') #7
In the definition heading for happyBirthday, person is referred to as a parameter, or a formal parameter. This variable name is a placeholder for the real name of the person being sung to.
The last two lines of the program, again, are the only ones outside of definitions, so they are the only ones executed directly. There is now an actual name between the parentheses in the function calls. The value between the parentheses here in the function call is referred to as an argument or actual parameter of the function call. The argument supplies the actual data to be used in the function execution. When the call is made, Python does this by associating the formal parameter name person with the actual parameter data, as in an assignment statement. In the first call, this actual data is 'Emily'. We say the actual parameter value is passed to the function.
The execution in greater detail:
The beauty of this system is that the same function definition can be used for a call with a different actual parameter variable, and then have a different effect. The value of the variable person is used in the third line of happyBirthday, to put in whatever actual parameter value was given.
This is the power of abstraction. It is one application of the most important principal in programming. Rather than have a number of separately coded parts with only slight variations, see where it is appropriate to combine them using a function whose parameters refer to the parts that are different in different situations while the code is written to be simultaneously appropriate for the separate specific situations, with the substitutions of the right parameter values.
You can go back to having a main function again, and everything works. Run birthday7.py:
def happyBirthday(person):
print "Happy Birthday to you!"
print "Happy Birthday to you!"
print "Happy Birthday, dear " + person + "."
print "Happy Birthday to you!"
def main():
happyBirthday('Emily')
happyBirthday('Andre')
main()
Exercise 1.11.4.1. * Make your own further change to the file and save it as birthdayMany.py: Add a function call, so Maria gets a verse, in addition to Emily and Andre. Also print a blank line between verses. (You may either do this by adding a print line to the function definition, or by adding a print line between all calls to the function.)
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:
def happyBirthday(person):
print "Happy Birthday to you!"
print "Happy Birthday to you!"
print "Happy Birthday, dear " + person + "."
print "Happy Birthday to you!"
def main():
userName = raw_input("Enter the Birthday person's name: ")
happyBirthday(userName)
main()
This last version illustrates several important ideas:
A function can have more than one parameter in a parameter list separated by commas. Here the example program addition5.py uses a function to make it easy to display many sum problems. Read and follow the code, and then run:
def sumProblem(x, y):
print 'The sum of %s and %s is %s.' % (x, y, x+y)
def main():
sumProblem(2, 3)
sumProblem(1234567890123, 535790269358)
a, b = input("Enter two comma separated numbers: ")
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 the function call with
actual parameters, f(actual1, actual2, actual3), calling the function f with definition heading
def f(formal1, formal2, formal3):
acts approximately as if the first lines executed inside the called function were
formal1 = actual1
formal2 = actual2
formal3 = actual3
Functions provide extremely important functionality to programs, allowing task to be defined once and performed repeatedly with different data. It is essential to see the difference between the formal parameters used to describe what is done inside the function definition (like x and y in the definition of sumProblem) ant the actual parameters (like 2 and 3 or 1234567890123 and 535790269358) which substitute for the formal parameters when the function is actually executed. The main method above uses three different sets of actual parameters in the three calls to sumProblem.
Exercise 1.11.5.1. '* Modify the program above and save it as quotientProb.py. The new program should have a quotientProblem function, printing as in the Exercise 1.10.4.2. The main method should test the function on several sets of literal values, and also test the function with input from the user.
You probably have used mathematical functions in algebra class, but they all had calculated values associated with them. For instance if you defined f(x) = x2, then it follows that f(3) is 32 = 9, and f(3) + f(4) is 32 + 42 = 25. Function calls in expressions get replaced during evaluation by the value of the function.
The corresponding definition and examples in Python would be the following, also in the example program return1.py. Read and run:
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, temporarily the function call is effectively replaced by its returned value. Inside the Python function, the value to be returned is given by the expression in the return statement. After the function f finishes executing from inside
print f(3)
it is as if the statement temporarily became
print 9
and similarly when executing
print f(3) + f(4)
the interpreter first evaluates f(3) and effectively replaces the call by the returned result, 9, as if the statement temporarily became
print 9 + f(4)
and then the interpreter evaluates f(4) and effectively replaces the call by the returned result, 16, as if the statement temporarily became
print 9 + 16
resulting finally in 25 being calculated and printed.
Python functions can return any type of data, not just numbers, and there can be any number of statements executed before the return statement. Read, follow, and run the example program return2.py:
def lastFirst(firstName, lastName): #1
separator = ', ' #2
result = lastName + separator + firstName #3
return result #4
print lastFirst('Benjamin', 'Franklin') #5
print lastFirst('Andrew', 'Harrington') #6
The code above has a new feature, variables separator and result are given a value in 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 Section 1.11.8 on local scope.
Details of the execution:
Compare return2.py and addition5.py, from the previous section. Both use functions. Both print, but where the printing is done differs. The function sumProblem prints directly inside the function and returns nothing explicitly. On the other hand lastFirst does not print anything but returns a string. The caller gets to decide what to do with the string, and above it is printed in the main program.
Open addition5.py again, and introduce a common mistake. Change the last line of the function main inserting print, so it says
print sumProblem(a, b)
Then try running the program. The desired printing is actually done inside the function sumProblem. You introduced a statement to print what sumProblem returns. Although sumProblem returns nothing explicitly, Python does make every function return something. If there is nothing explicitly returned, the special value None is returned. You should see that in the Shell output. This is a fairly common error. If you see a 'None' is your 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!
Exercise 1.11.6.1. Create quotientReturn.py by modifying quotientProb.py from Exercise 1.11.5.1 so that the program accomplishes the same thing, but everywhere change the quotientProblem function into one called quotientString that merely returns the string rather than printing the string directly. Have the main function print the result of each call to the quotientString function.
The remainder of Section1.11 covers finer points about functions that you might skip on a first reading.
We are only doing tiny examples so far to get the basic idea of functions. In much larger programs, functions are useful to manage complexity, splitting things up into logically related, modest sized pieces. Programmers are both writers of functions and consumers of the other functions called inside their functions. It is useful to keep those two roles separate:
The user of an already written function needs to know:
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.
For the logic of writing functions, it is important that the writer of a function knows the names of variables inside the function. On the other hand, if you are only using a function, maybe written by someone unknown to you, you should not care what names are given to values used internally in the implementation of the function you are calling. Python enforces this idea with local scope rules: Variable names initialized and used inside one function are invisible to other functions. Such variables are called local variables. For example, an elaboration of the earlier program return2.py might have its lastFirst function with its local variable separator, but it might also have another function that defines a separator variable, maybe with a different value like '\n'. They do not conflict. They are independent. This avoids lots of errors!
For example, the following code in the example program badScope.py causes an execution error. Read it and run it, and see:
def main():
x = 3
f()
def f():
print x #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, include parameters! Read and compare and try the program goodScope.py:
def main():
x = 3
f(x)
def f(x):
print x
main()
With parameter passing, the parameter name x in the function f does not need to match the name of the actual parameter in main. The definition of f could just as well have been:
def f(whatever):
print whatever
If you define global variables (outside of any function definition), they are visible inside all your functions. It is good programming practice to avoid defining global variables and instead to put your variables inside functions. One common exception is constants: A constant is a name that you give a fixed data value to, assigning a value to the name only in a single assignment statement, and then using the name of the fixed data value in expressions later. A simple example program is constant.py:
PI = 3.14159265358979 # global constant -- only place the value of PI is set
def circleArea(radius):
return PI*radius*radius # use value of global constant PI
def circleCircumference(radius):
return 2*PI*radius # use value of global constant PI
print 'circle area with radius 5:', circleArea(5)
print 'circumference with radius 5:', circleCircumference(5)
This example uses numbers with decimal points, discussed more in Section 1.14.1.
There are further issues with global variables. A discussion is postponed.
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.
In common usage, a dictionary is a collection of words matched with their definitions. Given a word, you can look up its definition. Python has a built in dictionary type called dict which you can use to create dictionaries with arbitrary definitions for character strings. It can be used for the common usage, as in a simple English-Spanish dictionary.
Look at the example program spanish1.py and run it.
"""A tiny English to Spanish dictionary is created,
using the Python dictionary type dict.
Then the dictionary is used, briefly.
"""
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'
print spanish['two']
print spanish['red']
First an empty dictionary is created using dict(), and it is assigned the 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 statements), where its definition is one stored earlier into the dictionary. For example,
spanish['hello'] = 'hola'
makes an entry in our spanish dictionary for 'hello' , where the definition matched to it is 'hola'.
print spanish['red']
retrieves the definition for 'red', which is 'rojo'.
Since the Spanish dictionary is defined at the top-level, the variable name spanish is still defined after the program runs: after running the program, use spanish in the Shell to check out the translations of some more words, other than 'two' and 'red'.
Creating the dictionary is quite a different activity from the use at the end of the code, so with functions to encapsulate the tasks, we could write the example program spanish2.py instead, with the same result:
"""A tiny English to Spanish dictionary is created,
using the Python dictionary type dict.
Then the dictionary is used, briefly.
"""
def createDictionary():
'''Returns a tiny Spanish dictionary'''
spanish = dict() # creates an empty dictionary
spanish['hello'] = 'hola'
spanish['yes'] = 'si'
spanish['one'] = 'uno'
spanish['two'] = 'dos'
spanish['three'] = 'tres'
spanish['red'] = 'rojo'
spanish['black'] = 'negro'
spanish['green'] = 'verde'
spanish['blue'] = 'azul'
return spanish
def main():
dictionary = createDictionary()
print dictionary['two']
print dictionary['red']
main()
This code illustrates several things about functions.
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. 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.
Exercise 1.12.1.1. * Write a tiny Python program numDict.py that makes a dictionary whose keys are the words 'one', 'two', 'three', and 'four', and whose corresponding values are the numerical equivalents, 1, 2, 3, and 4 (ints, not strings). Include code to test the resulting dictionary by referencing several of the definitions and printing the results.
Dictionaries that have keys of string type can be used in a special way with format strings. Rather than choosing substitutions by positional sequence in a tuple, lookups can be made using keys in the dictionary. We reuse the createDictionary function from Section1.12.1, and just change the print statements in the main function. Read and run spanish3.py:
"""Illustrate using a dictionary with a format string."""
def createDictionary(): # same old function
'''Returns a tiny spanish dictionary'''
spanish = dict() # creates an empty dictionary
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 "Count in Spanish: %(one)s, %(two)s, %(three)s, ..." % dictionary
print "Spanish colors: %(red)s, %(blue)s, %(green)s, ..." % dictionary
main()
Within the format strings, where there is a % signaling a substitution, it must be immediately followed by a dictionary key name in parentheses and then the rest of the format specifier (which in all the examples here, is just s indicating a string value), so in this example the format is always %(key)s. Note that the key is not in quotes. After the format string comes another % character as usual for the format operation, but it must be followed be just a single dictionary, not a tuple.7
Choosing between the tuple and dictionary forms of formatting: For format strings with only a few substitutions, and where the format string is not likely to be edited and rearranged much, the tuple format is shorter and simpler. If you actually want to substitute from a dictionary anyway, the dictionary format is obvious. If you have a long format string that you might want to edit and rearrange, or repeat the use the same substituted value several times, the dictionary formulation is likely convenient , even if you have to create the dictionary just for use in the format string.
At this point we have discussed in some detail everything that went into the first sample program, madlib.py, of Section 1.2.3! 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 1.2.3 again (or for the first time): it should make much more sense now.
Exercise 1.12.2.1. To confirm your better understanding of madlib.py, load it in the editor, rename it as myMadlib.py, and modify it to have a less lame story, with more and different entries in the dictionary. Make sure addPick is called for each key in your format string. Test your version.
We will use madlib.py as a basis for more substantial modifications in structure in Section 2.3.3.
Modern computers can do millions or even billions of instructions a second. With the techniques discussed so far, it would be hard to get a program that would run by itself for more than a fraction of a second.8 Practically, we cannot write millions of instructions to keep the computer busy. To keep a computer doing useful work we need repetition, looping back over the same block of code again and again. There are two Python statement types to do that: the simpler for loops, which we take up shortly, and while loops, which we take up later, in Section 3.3. Two preliminaries: First, the value of already defined variables can be updated. This will be particularly important in loops. We start by following how variables can be updated in an even simpler situation. Second, for loops involve sequence types, so we will first look at a basic sequence type: list. This is a long section. Go carefully.
The programs so far have defined and used variables, but other than in early shell examples we have not changed the value of existing variables. For now consider a particularly simple example, just chosen as an illustration, in the example file updateVar.py:
x = 3 #1
y = x + 2 #2
y = 2*y #3
x = y - x #4
print x, y #5
Can you predict the result? Run the program and check. Particularly if 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 are made by each statement. In the table below, statements are referred to by the numbers labeling the lines in the code above. We can track the state of each variable after each line in executed. A dash is shown where a variable is not defined. For instance after line 1 is executed, a value is given to x, but y is still undefined. Then y gets a value in line 2. The comment on the right summarizes what is happening. Since x has the value 3 when line 2 starts, x+2 is the same as 3+2. In line three we use the fact that the right side of an assignment statement uses the values of variables when the line starts executing (what is left after the previous line of the table executed), but the assignment to the variable y on the left causes a change to y, and hence the updated value of y, 10, is shown in the table. Line 4 then changes x, using the latest value of y (10, not the initial value 5!). The result from line 5 confirms the values of x and y.
Line | x | y | comment |
1 | 3 | - | |
2 | 3 | 5 | 5=3+2, using the value of x from the previous line |
3 | 3 | 10 | 10=2*5 on the right, use the value of y from the previous line |
4 | 7 | 19 | 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 order, 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.
Lists are ordered sequences of arbitrary data. Tuples, that were mentioned briefly in connection with string formatting, satisfy the same description. The difference is that lists are mutable: the length of the sequence can be changed and elements substituted, unlike tuples. We will delay the discussion of changes to lists until a further introduction to objects. Lists can be written explicitly. Read the following examples
['red', 'green', 'blue']
[1, 3, 5, 7, 9, 11]
['silly', 57, 'mixed', -23, 'example']
[] # the empty list
The basic format is square-bracket-enclosed, comma-separated lists of arbitrary data (not parenthesis-enclosed as in tuples).
There is a built-in function range, that can be used to automatically generate lists that are regular arithmetic sequences. Try the following in the Shell:
range(4)
range(10)
The general pattern is
range(sizeOfList)
As you can see, you get a list returned, starting from 0, and ending one before the parameter. We will see there are good reasons to start from 0 in Python. One important property of lists generated by range(n) is that the total number of elements is n. The list omits the number n itself, but includes 0 instead.
With more parameters, the range function can be used to generate a much wider variety of sequences. The elaborations are discussed in Section 2.4.12 and Section 3.3.2.
Try the following in the Shell. You get a sequence of continuation lines before the Shell responds. The second and third lines should be automatically indented by the Shell, and be sure to enter another empty line (just Enter) at the end to get the Shell to respond.
for count in [1, 2, 3]:
print count
print 'Yes' * count
This is a for loop. It has the heading starting with for, followed by a variable name (count in this case), the word in, some sequence, and a final colon. As with function definitions and other heading lines ending with a colon, the colon at the end of the line indicates that a consistently indented block of statements follows to complete the for loop.
for item in sequence:
indented statements to repeat
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 i is 1, then 2, and finally 3. Look again at the output and see that it matches this sequence.
There is a reason the interpreter waited to respond until after you entered an empty line: The interpreter did not know how long the loop block was going to be! The empty line is a signal to the interpreter that you are done with the loop block.
Look at the following example program for123.py, and run it.
for count in [1, 2, 3]: #1
print count #2
print 'Yes'*count #3
print 'Done counting.' #4
for color in ['red', 'blue', 'green']: #5
print color #6
In a file, where the interpreter does not need to respond immediately, the blank line is not necessary. Instead, as with a function definition or any other format with an indented block, you indicate being past the indented block by dedenting to line up with the for-loop heading. Hence in the code above, “Done Counting.” is printed once after the first loop completes all its repetitions. Execution ends with another simple loop.
As with the indented block in a function, it is important to get the indentation right. Alter the code above, so line 4 is indented:
for count in [1, 2, 3]: #1
print count #2
print 'Yes'*count #3
print 'Done counting.' #4
for color in ['red', 'blue', 'green']: #5
print color #6
Predict the change, and run the code again to test.
Loops are one of the most important features in programming. While the 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. One of the simplest patterns is illustrated above, simple for-each loops.
for item in sequence
do some thing with item
(It would be even more like English if for were replace by for each, but the shorter version is the one used by Python.)
In the for-loop examples above, something is printed that is related to each item in the list. Printing is certainly one form of “do something”, but the possibilities for “do something” are completely general.
We can use a for-each loop to revise our first example. Recall the code from madlib.py:
addPick('animal', userPicks)
addPick('food', userPicks)
addPick('city', userPicks)
Each line is doing exactly the same thing, except varying the string used as the cue, while repeating the rest of the line. This is the for-each pattern, but we need to list the sequence that the cues come from. Read the alternative:
for cue in ['animal', 'food', 'city']: # heading
addPick(cue, userPicks) # body
If you wish to see or run the whole program with this small modification, see the example madlibloop.py.
It is important to understand the sequence of operations, how execution goes back and forth between the heading and the body. Here are the details:
This looping construction would be even 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 Section 2.3.3, after we introduce more about string manipulation.
The examples above all used the value of the variable in the for-loop heading. An even simpler for-loop usage is when you just want to repeat the exact same thing a specific number of times. In that case only the length of the list, not the individual elements are important. We have already seen that the range function provides an ease way to produce a list with a specified number of elements. Read and run the example program repeat1.py:
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:
n = input('Enter the number of times to repeat: ')
for i in range(n):
print 'This is repetitious!'
Suppose I have a list of items called items, and I want to print out each item and number them successively. For instance if items is ['red', 'orange', 'yellow', 'green'], I would like to see the output:
1 red
2 orange
3 yellow
4 green
Read about the following thought process for developing this:
If I allow myself to omit the numbers, it is easy: For any item in the list, I can process it with
print item
and I just go through the list and do it for each one. (Copy and run if you like.)
items = ['red', 'orange', 'yellow', 'green']
for item in items:
print item
Clearly the more elaborate version with numbers has a pattern with some consistency, each line is at least in the form:
number item
but the number changes each time, and the numbers do not come straight from the list of items.
A variable can change, so it makes sense to have a variable number, so we have the potential to make it change correctly. We could easily get it right the first time, and then repeat the same number. Read and run the example program numberEntries1.py:
items = ['red', 'orange', 'yellow', 'green']
number = 1
for item in items:
print number, item
Of course this is still not completely correct, since the idea was to count. after the first time number is printed, it needs to be changed to 2 to be right the next time through the loop. This will make 2 appear correctly in the second output line: Read and run the example program numberEntries2.py:
items = ['red', 'orange', 'yellow', 'green']
number = 1
for item in items:
print number, item
number = 2
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 bodyto get the example program numberEntries3.py. See the addition and run it:
items = ['red', 'orange', 'yellow', 'green'] #1
number = 1 #2
for item in items: #3
print number, item #4
number = number + 1 #5
It is important to understand the step-by-step changes during execution. Below is another table showing the results of playing computer. The line numbers are much more important here to keep track of the flow of control, because of the jumping around at the end of the loop.
Line | items | item | number | comment |
1 | ['red', 'orange', 'yellow', 'green'] | - | - | |
2 | ['red', 'orange', 'yellow', 'green'] | - | 1 | |
3 | ['red', 'orange', 'yellow', 'green'] | 'red' | 1 | start with item as first in sequence |
4 | ['red', 'orange', 'yellow', 'green'] | 'red' | 1 | print: 1 red |
5 | ['red', 'orange', 'yellow', 'green'] | 'red' | 2 | 2 = 1+1 |
3 | ['red', 'orange', 'yellow', 'green'] | 'orange' | 2 | on to the next element in sequence |
4 | ['red', 'orange', 'yellow', 'green'] | 'orange' | 2 | print 2 orange |
5 | ['red', 'orange', 'yellow', 'green'] | 'orange' | 3 | 3=2+1 |
3 | ['red', 'orange', 'yellow', 'green'] | 'yellow' | 3 | on to the next element in sequence |
4 | ['red', 'orange', 'yellow', 'green'] | 'yellow' | 3 | print 3 yellow |
5 | ['red', 'orange', 'yellow', 'green'] | 'yellow' | 4 | 4=3+1 |
3 | ['red', 'orange', 'yellow', 'green'] | 'green' | 4 | on to the last element in sequence |
4 | ['red', 'orange', 'yellow', 'green'] | 'green' | 4 | print 4 green |
5 | ['red', 'orange', 'yellow', 'green'] | 'green' | 5 | 5=4+1 |
3 | ['red', 'orange', 'yellow', 'green'] | 'green' | 5 | sequence done, end loop and code |
The final value of number is never used, but that is OK. What we want is printed.
This short example illustrates a lot of ideas:
There is a general pattern to loops with successive modification of a variable like number above:
This information can be put in a code outline:
Initialize variables to be modified
Loop heading controlling the repetition
Do the desired action with the current variables
Modify variables to be ready for the action the next time
If you compare this pattern to the for-each and simple repeat loops in Section 1.13.4, 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 follow the whole sequence! This program has the most complicated flow of control so far, changing both for function calls and loops.
Suppose you want to add up all the numbers in a list, nums. Let us plan this as a function from the beginning, so read the code below. We can start with:
def sumList(nums):
'''Return the sum of the numbers in nums.'''
If you do not see what to do right away, a useful thing to do is write down a concrete case, and think how you would solve it, in complete detail. If nums is ['2', '6' ,'3', '8'], you would likely calculate
2+6 is 8
8 + 3 is 11
11 + 8 is 19
19 is the answer to be returned.
Since the list may be arbitrarily long, you need a loop. Hence you must find a pattern so that you can keep reusing the same statements in the loop. Obviously you are using each number in the sequence in order. You also generate a sum in each step, which you reuse in the next step. The pattern is different, however, in the first line, 2+6 is 8: there is no previous sum, and you use two elements from the list. The 2 is not added to a previous sum.
Although it is not the shortest way to do the calculation by hand, 2 is a sum of 0 + 2: We can make the pattern consistent and calculate:
start with a sum of 0
0 + 2 is 2
2 + 6 is 8
8 + 3 is 11
11 + 8 is 19
19 is the answer.
Then the second part of each sum is a number from the list, nums. If we call the number from the list num, the main calculation line in the loop could be
nextSum = sum + num
The trick is to use the same line of code the next time through the loop. That means what was nextSum in one pass becomes the sum in the next pass. One way to handle that is:
sum = 0
for num in nums:
nextSum = sum + num
sum = nextSum
Do you see the pattern? Again it is
initialization
loop heading
main work to be repeated
preparation for the next time through the loop
Sometimes the two general loop steps can be combined. This is such a case. Since nextSum is only used once, we can just substitute its value where it is used and simplify to:
sum = 0
for num in nums:
sum = sum + num
so the whole function, with the return statement is:
def sumList(nums): #1
'''Return the sum of the numbers in nums.'''
sum = 0 #2
for num in nums: #3
sum = sum + num #4
return sum #5
With the following (not indented) line below used to test the function, you have the example program sumNums.py. Run it.
print sumList([5, 2, 4, 7])
The pattern used here is certainly successive modification (of the sum variable). It is useful to giver a more specialized name for this version of the pattern here. It follows an accumulation pattern:
initialize the accumulation to include none of the sequence (sum = 0 here)
for item in sequence :
new value of accumulation = result of combining item with last value of accumulation
This pattern will work in many other situations besides adding numbers.
Exercise 1.13.7.1. * Suppose the function sumList, is called with the parameter [5, 2, 4, 7]. Play computer on this call. Make sure there is a row in the table for each line executed in the program, each time it is executed. In each row enter which program line is being executed and show all changes caused to variables by the execution of the line. A table is started for you below. The final line of your table should be for line 5, with the comment, “return 18”. If you do something like this longhand, and the same long value repeats a number of times, it is more convenient to put a ditto (“) for each repeated variable value or even leave it blank. If you want to do it on a computer you can start from the first table in example file playComputerSumStub.rtf. First save the file as playComputerSum.rtf.
Line | nums | sum | num | comment |
1 | [5, 2, 4, 7'] | - | - | |
2 | ||||
Exercise 1.13.7.2. * Write a program testSumList.py which includes a main function to test the sumList function several times. Include a test for the extreme case, with an empty list.
Exercise 1.13.7.3. ** Complete the following function. This starting code is in joinAllStub.py. Save it to the new name joinAll.py. Note the way an example is given in the documentation string. It simulates the use of the function in the Shell. This is a common convention:
def joinStrings(stringList):
'''Join all the strings in stringList into one string,
and return the result. For example:
>>> print joinStrings(['very', 'hot', 'day'])
'veryhotday'
'''
Testing code by running it is fine, but looking at the results does not mean you really understand what is going on, particularly if there is an error! People who do not understand what is happening are likely to make random changes to their code in an attempt to fix errors. This is a very bad, increasingly self-defeating practice, since you are likely to never learn where the real problem lies, and the same problem is likely to come back to bite you.
It is important to be able to predict accurately what code will do. We have illustrated playing computer on a variety of small chunks of code.
Playing computer can help you find bugs (errors in your code). Some errors are syntax errors caught by the interpreter in translation. Some errors are only caught by the interpreter during execution, like failing to have a value for a variable you use. Other errors are not caught by the interpreter at all – you just get the wrong answer. These are called logical errors. Earlier logical errors can also trigger an execution error later. This is when playing computer is particularly useful.
A common error in trying to write the numberList function would be to have:
def numberList(items): # WRONG code for illustration!!!! #1
'''Print each item in a list items, numbered in order.''' #2
for item in items: #3
number = 1 #4
print number, item #5
number = number + 1 #6
You can run this code in numberEntriesWRONG.py and see that it produces the wrong answer. If you play computer on the call to numberList(['apples', 'pears', 'bananas']), you can see the problem:
Line | items | item | number | comment |
1 | ['apples', 'pears', 'bananas'] | - | - | pass actual parameter value to items |
3 | ['apples', 'pears', 'bananas'] | 'apples' | - | start with item as first in sequence |
4 | ['apples', 'pears', 'bananas'] | 'apples' | 1 | |
5 | ['apples', 'pears', 'bananas'] | 'apples' | 1 | print: 1 apples |
6 | ['apples', 'pears', 'bananas'] | 'apples' | 2 | 2 = 1+1 |
3 | ['apples', 'pears', 'bananas'] | 'pears' | 2 | on to the next element in sequence |
4 | ['apples', 'pears', 'bananas'] | 'apples' | 1 | |
5 | ['apples', 'pears', 'bananas'] | 'pears' | 1 | print: 1 pears OOPS! |
If you go step by step you should see where the incorrect 1 came from: the initialization is repeated each time in the loop at line 4, undoing the incrementing of number in line 6, messing up your count. Always be careful that your one-time initialization for a loop goes before the loop, not in it!
Functions can also return values. Consider the Python for this mathematical sequence: define the function m(x) = 5x, let y = 3; find m(y) + m(2y-1).
def m(x): #1
return 5*x #2
y = 3 #3
print m(y) + m(2*y-1) #4
A similar example was considered in Section 1.11.6, but now add the idea of playing computer and recording the sequence in a table. Like when you simplify a mathematical expression, Python must complete the innermost parts first. Tracking the changes means following the function calls carefully and using the values returned. Again a dash '-' is used in the table to indicate an undefined variable. Not only are local variables like formal parameters undefined before they are first used, they are also undefined after the termination of the function,
Line | x | y | comment |
3 | - | 3 | (definitions before this) |
4 | - | 3 | start on: print m(y) + m(2*y-1); find m(y), which is m(3) |
1 | 3 | 3 | pass 3 to function m, so x =3 |
2 | 3 | 3 | return 5*3 = 15 |
4 | - | 3 | substitute result: print 15 + m(2*y-1), find 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 |
4 | - | 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.
Exercise 1.13.8.1. ** Play computer on the following code. Reality check: 31 is printed when line 6 finally executes. Table headings are shown below to get you started with a pencil. Alternately you can work in a word processor starting from playComputerStub.rtf, which has tables set up for this and the following exercise. Save the file with an alternate name playComputer.rtf.
x = 0 #1
y = 1 #2
for n in [5, 4, 6]: #3
x = x + y*n #4
y = y + 1 #5
print x #6
Line | x | y | n | Comment |
Exercise 1.13.8.2. ** The following code is supposed to compute the product of the numbers in a list. For instance product([5, 4, 6]) should calculate and return 5*4*6=120 in steps, calculating 5, 5*4=20 and 20*6=120 . Play computer on a call to product([5, 4, 6]) until you see that it makes a mistake. This code appears in the example file numProductWrong.py. Save it as numProduct.py and fix the error (and save again!). Table headings and the first row are shown below to get you started with a pencil. Alternately you can work in a word processor continuing to add to playComputer.rtf, started in the previous exercise.
def product(nums): #1
for n in nums: #2
prod = 1 #3
prod = prod*n #4
return prod #5
Line | nums | n | prod | Comment |
1 | [5, 4, 6] | - | - | |
Exercise 1.13.8.3. ** Play computer on the following code. Table headings are shown for you. Reality check: 70 is printed. See the previous exercises if you enter your answer in a file.
def f(x): #1
return x+4 #2
print f(3)*f(6) #3
Line | x | Comment |
There is also a version of the print statement ending with a comma, that prints one or more items, but does not advance to a new line. Read the illustrations:
print 'all', 'on', 'same', 'line'
print 'different line'
is equivalent to
print 'all', 'on', # note final comma
print 'same', # note final comma
print 'line' # NO final comma
print 'different line'
This does not work directly in the shell (where you are always forced to a new line at the end). It does work in a program, but it is not very useful except in a loop. Suppose I want to print a line with all the elements of a list, separated by spaces, but not on separate lines. I can use a comma in the loop. Can you figure out in your head what this example file comma1.py does? Then try it:
def listOnOneLine(items):
for item in items:
print item,
listOnOneLine(['apple', 'banana', 'pear'])
print 'This may not be what you expected!'
If you still want to go on to a new line at the end of the loop, you must include a print statement that does advance to the next line, once, after the loop. Try this variation, comma2.py:
def listOnOneLine(items):
for item in items:
print item,
print
listOnOneLine(['apple', 'banana', 'pear'])
print 'This is probably better!'
Floating point numbers like 12.345 are a basic type, but there are some complications due to their inexactness and the details of mixing them with integers. This section may be deferred until you actually need numbers other than integers.
As you moved on in school after your first integer division, and did fractions and decimals, you probably thought of 6/8 as a fraction and could convert to a decimal .75. Clearly we want Python to be able to do decimal calculations, too. Still, in Python, integer division 6/8 is 0. To get the decimal result, the trick is to include a decimal point in at least one of the operands to the division.
Try all set-off lines in this section in the Shell:
6.0/8
6/8.0
2.3/25.7
There is more going on here than meets the eye. As you should know, decimal representations of values can be a pain. They may not be able to be expressed with a finite number of characters. Try
2.0/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 Section 1.1?) Also floats use an encoding something like scientific notation from science class, with exponents that allow the decimal point to move or “float”, as in the decimal case: 2345:6 = (2:3456)103
Try
type(-2)
type(-2.0)
Even a number that is actually an integer can be represented in the float type if a decimal point is included.
Always be sure to remember that floats may not be exact. The use of base two makes this true even in cases where decimal numbers can be expressed exactly! Try
.6
Just as division uses real number (float) arithmetic if at least one of the operands is a float, the same is true for the other operations (though the result is not so dramatic). Try each in the Shell (and guess the resulting type):
3.3 - 1.1
2.0 + 3
2.5*2
Python makes the result of any single arithmetic operation be a float if at least one of the two operands is a float.
It is still easy to type something like the following when you mean something different. Guess the result first and test:
(1/2)*6.5
Although the whole expression has a float in it, precedence says the division operation inside the parentheses is done first, and each of its operands are integers.... What is 1/2 in Python? Try just that part.... Modify the expression to (1.0/2)*6.5 and try again.
Exponentiation is finding powers. In mathematical notation, (3)(3)(3)(3) = 34 = 81: In Python there is no fancy typography with raised exponent symbols like the 4, so Python uses ** before a power: Try in the Shell:
3**4
5*2**3
If you expected 1000 for the second one, remember exponentiation has even higher precedence than multiplication and division: 2**3 is 2*2*2 or 8, and 5*8 is 40.
Exponents do not need to be integers. A useful example is the 0.5 power: it produces a square root. Try in the Shell:
9**.5
2**.5
The result is of int type only if both parameters are integers and the correct result is an integer.
You generally do not want to display a floating point result of a calculation in its raw form, often with an enormous number of digits after the decimal point, like 23.457413902458498. You are likely to prefer rounding it to something like 23.46. There is special format string syntax for floats to accomplish that. Read the following example interpreter sequence:
>>> x = 23.457413902458498
>>> print 'x rounded to 2 places: %.2f, to 5 places: %.5f' % (x, x)
x rounded to 2 places: 23.46, to 5 places: 23.45741
Note that the results are rounded not truncated, the result to two places is 23.46, not 23.45. The general rule here is to indicate the data is a float rather than string type by ending its format specifiers with f for float, and putting a decimal point follow by a non negative integer between the % and f, so the form is %.#f, where # should be replaced by a non negative integer representing the number of places beyond the decimal point.
Predict what the following code will do and check by running example program floatFormat.py:
x = 2.876543
print 'longer: %.5f, shorter: %.3f' % (x, x)
Exercise 1.14.3.1. * Write a program, discount.py, that prompts the user for an original price and for a discount percentage and prints out the new price to the nearest cent. For example if the user enters 2.89 for the price and 20 for the discount percentage, the value would be (1- 20/100.0)*2.89, rounded to two decimal places, 2.31. For price .65 with a 25 percent discount, the value would be (1- 25/100.0)*.65, rounded to two decimal places, .49. Note the decimal point in the 100.0 used to convert the percentage to a fraction. This is important to make sure the calculation is done with floats, not integers! Write the general calculation code following the pattern of the calculations illustrated in the two concrete examples.
Section numbers in square brackets indicate where an idea was first discussed.
Where Python syntax is illustrated, the typeface indicates the the category of each part:
Typeface | Meaning |
Typewriter font | Text to be written verbatim |
Emphasized | A place where you can use an arbitrary identifier. The emphasized text attempts to be descriptive of the meaning of the identifier in the current context. |
Normal text | A description of what goes in that position, without giving explicit syntax |
If there are several variations on a particular part of the syntax, alternatives will be show on successive lines.
To emphasize the successive parts of the syntax, space will generally be left around symbol and punctuation characters, but the space is not required in actual use.
substitute the string version of the associated value [1.10.2]
where # can be any literal non negative integer: substitute a numerical value rounded to the specified number of places beyond the decimal point. [1.14.3]
Example: 'A word: %s, a number: %s, a formatted number: %.3f.' % ('Joe', 23,
2.13579)
evaluates to: 'A word: Joe, a number: 23, a formatted number: 2.136.'
for item in sequence :
consistently indented statement block, which may use the variable item
For each element in the sequence, repeat the statement block substituting the next element in the sequence for the name variable name item. See Programming Patterns for patterns of use. [1.13.4]
def functionName ( parameter1, parameter2, and so on) :
consistently indented statement block, which may include a return statement
for i in range(n):
actions to be repeated
Here the variable i is included only because there must be a variable name in a for-loop. [1.13.5]
for item in sequence :
actions to be done with each item
initialize all variables that will be successively modified in the loop
loop heading for the repetition :
actions to be in each loop with the current variable values
modify the variable values to prepare for the next time through the loop
initialize the accumulation to include none of the sequence
for item in sequence :
new value of accumulation =
result of combining item with last value of accumulation
Python is an object-oriented language. Every piece of data and even functions and types are objects. The term object-oriented is used to distinguish Python from earlier languages, classified as procedural languages, where types of data and the operations on them were not connected in the language. The functions we have used so far follow the older procedural programming syntax. In the newer paradigm of object-oriented programming, all data are in objects, and a core group of operations that can be done on some particular type of object are tightly bound to the object and called the object's methods.
For example, strings are objects, and strings “know how” to produce an uppercase version of themselves. Try in the Shell:
s = 'Hello!'
s.upper()
Here upper is a method associated with strings. This means upper is a function that is bound to the string before the dot. This function is bound both logically, and as we see in the new notation, also syntactically. One way to think about it is that each type of data knows operations (methods) that can be applied to it. The expression s.upper() calls the method upper that is bound to the string s and returns a new uppercase string result based on s.
Strings are immutable, so no string method can change the original string, it can only return a new string. Confirm this by entering each line individually in the Shell to see the original s is unchanged:
s
s2 = s.upper()
s2
s
We are using the new object syntax:
object.method( )
meaning that the method associated with the object's type is applied to the object. This is just a special syntax for a function call with an object.
Another string method is lower, analogous to upper, but producing a lowercase result.
Test yourself : How would you write the expression to produce a lowercase version of the string s?
Answer:1
Try it in the Shell.
Test yourself in the Shell: How would you use this string s and both the lower and upper methods to create the string
'hello!HELLO!' ? Hint: 2
Answer: 3
Many methods also take additional parameters between the parentheses, using the more general syntax
object.method(parameters)
The first of many such methods we will introduce is count:
Syntax for count:
s.count(sub)
Count and return the number of repetitions of a string sub that appear as substrings inside the string s.
Read and make sure you see the answers are correct:
>>> tale = 'This is the best of times.'
>>> tale.count('i')
3
>>> tale.count('is')
2
>>> tale.count('That')
0
>>> tale.count(' ')
5
There is a blank between the quotes in the line above. Blanks are characters like any other (except you can't see them)!
Just as the parameter can be replaced by a literal or any expression, the object to which a method is bound with the dot may also be given by a literal, or a variable name, or any expression that evaluates to the right kind of object in its place. This is true for any method call.
Technically the dot between the object and the method name is an operator, and operators have different levels of precedence. It is important to realize that this dot operator has the highest possible precedence. Read and see the difference parentheses make in the expressions:
>>> 'hello ' + 'there'.upper()
'hello THERE'
>>> ('hello ' + 'there').upper()
'HELLO THERE'
To see if you understand this precedence, predict the results of each line and then test in the Shell:
3 * 'X'.count('XXX')
(3 * 'X').count('XXX')
There are 0 'XXX's in 'X', but 1 'XXX' in 'XXX'.
Python lets you see all the methods that are bound to an object (and any object of its type) with the built-in function dir. To see all string methods, supply the dir function with any string. For example, try in the Shell:
dir('')
Many of the names in the list start and end with two underscores, like __add__. These are all associated with methods and pieces of data used internally by the Python interpreter. You can ignore them for now. The remaining entries in the list are all user-level methods for strings. You should see lower and upper among them. Some of the methods are much more commonly used than others.
Object notation
object.method(parameters)
has been illustrated so far with just the object type str, but it applies to all types. Later in the tutorial methods such as the following will be discussed:
If seq is a list, seq.append(element) appends element to the end of the list.
If myData is a file, myData.read() will read and return the entire contents of the file....
A string is a sequence of smaller components (individual characters), and it is often useful to deal with parts of strings. Python indexes the characters in a string, starting from 0, so for instance, the characters in the string 'computer' have indices:
character | c | o | m | p | u | t | e | r |
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Each index is associated with a character, and you reference the individual characters much like in a dictionary. Try the following. (You can skip the comments that make the indices explicit.) Enter in the Shell:
# 01234567
s = 'computer'
s[0]
s[5]
s[8]
You cannot refer directly to a character that is not there. Indices only go to 7 in the example above.
Recall the len function, which gives the length of a sequence. It works on strings. Guess the following value, and test in the Shell:
len(s)
A common error is to think the last index will be the same as the length of the string, but as you saw above, that leads to an execution error. If the length of some string is 5, what is the index of its last character? What if the length is 35?
Hopefully you did not count by ones all the way from 0. The indices for a string of length n are the elements of the list range(n), which goes from 0 through n-1, or the length of the string minus one, which is 5-1=4 or 35-1 = 34 in these examples.
Sometimes you are interested in the last few elements of a string and do not want to do calculations like this. Python makes it easy. You can index from the right end of the string. Since positive integers are used to index from the front, negative integers are used to index from the right end, so the more complete table of indices for 'computer' gives two alternatives for each character:
character | c | o | m | p | u | t | e | r |
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
index from the right end | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 |
Predict and test each individual line, continuing in the Shell:
s[-1]
s[-3]
s[-10]
it = 'horse'
len(it)
it[-1]
it[1]
Be careful - remember what the initial index is!
It is also useful to extract larger pieces of a string than a single character. That brings us to slices. Try this expression using slice notation, continuing in the Shell:
s[0:4]
Note that s[4] is the first character past the slice. The simplest syntax for a slice of a string s is:
s[startIndex : pastIndex]
This refers to the substring of s starting at index startIndex and stopping just before index pastIndex. It confuses
many people that the index after the colon is not the index of the final character in the slice, but that is the system.
Predict and try each line individually in the Shell:
s[2:5]
s[1:3]
If you omit the first index, the slice starts from the beginning. If you omit the second index, the slice goes all the way to the end. Predict and try each line individually in the Shell:
s[:3]
s[5:]
Predict and try each line individually in the Shell:
word = 'program'
word[2:4]
word[1:-3]
word[3:]
word[3:3]
word[:1] + word[4:]
Python evaluates slices in a more forgiving manner than when indexing single characters. In a slice, if you give an index past a limit of where it could be, Python assumes you mean the actual end. Predict and try each line individually in the Shell:
word[:9]
word[8:10]
Enter a slice expression using the variable word from above that produces 'gra'.
A useful string method that uses the ideas of indices and slices is find.
Syntax options for find:
s.find(sub)
s.find(sub, start)
s.find(sub, start, end)
Return the integer index in the string s of the beginning of first complete occurrence of the substring sub. If sub does not appear inside s, return -1. The value -1 would be an impossible result if sub were found, so if -1 is returned, sub must not have been found. If parameters start and end are not included in the parameter list, the search is through the whole string s. If an integer value is given for start, the search starts at index start. If an integer value is given for end, the search ends before index end. In other words if start and end appear, then the search is through the slice s[start : end], but the index returned is still counted from the beginning of s.
For example, check that the following make sense. The comment line is just there to help you count:
>>> # 01234567890
>>> s = 'Mississippi'
>>> s.find('i')
1
>>> s.find('si')
3
>>> s.find('sa')
-1
>>> s.find('si', 4)
6
Predict and try each line in the Shell:
# 0123456789012
line = 'Hello, there!'
line.find('e')
line.find('he')
line.find('e', 10)
line.find('he', 10)
We will consider more string methods later, but we can already do useful things with the ones introduced.
Inside the Shell, you can look up documentation on any of the methods listed with the dir function. Here is a place that you want to refer to the method itself, not invoke the method, so note that you get help for s.find not for s.find(). Assuming you defined the string s in the Shell earlier, try in the Shell
help(s.find)
The Python documentation uses square brackets to indicate optional elements that get a default value if you leave them out. This shortens the syntax descriptions.
If you want method documentation when you do not have a variable of the type created, you can also use the type name. Try in the Shell:
dir(str)
help(str.capitalize)
Indexing and slicing works on any kind of Python sequence, so you can index or slice lists and tuples also. Read this Shell session:
>>> vals = [5, 7, 9, 22, 6, 8]
>>> vals[1]
7
>>> vals[-2]
6
>>> vals[1:4]
[7, 9, 22]
Unlike strings, lists are mutable, as you will see in Section 2.2.1. 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.
All the concrete examples in the last two sections used literal numbers for the indices. That is fine for learning the idea, but in practice, variables or expressions are almost always used for indices. As usual the variable or expression is evaluated before being used. Try in Idle and see that the example program index1.py makes sense:
s = 'word'
print 'The full string is: ', s
n = len(s)
for i in range(n):
print
print 'i =', i
print 'The letter at index i:', s[i]
print 'The part before index i (if any):', s[:i]
print 'The part before index i+2:', s[:i+2]
We will use index variables in more practical situations as we explain more operations with strings.
Syntax options for the split method with a string s:
s.split()
s.split(sep)
The first version splits s at any sequence of whitespace (blanks, newlines, tabs) and returns the remaining parts of s as a list. If a string sep is specified, it is the separator that gets removed from between the parts of the list.
For example, read and follow:
>>> tale = 'This is the best of times.'
>>> tale.split()
['This', 'is', 'the', 'best', 'of', 'times.']
>>> s = 'Mississippi'
>>> s.split('i')
['M', 'ss', 'ss', 'pp', '']
>>> s.split() # no white space
['Mississippi']
Predict and test each line in the Shell:
line = 'Go: Tear some strings apart!'
seq = line.split()
seq
line.split(':')
line.split('ar')
lines = 'This includes\nsome new\nlines.'
lines.split()
Join is roughly the reverse of split. It joins together a sequence of strings. The syntax is rather different. The separator sep comes first, since it has the right type (a string).
Syntax for the join method:
sep.join(sequence)
Return a new string obtained by joining together the sequence of strings into one string, interleaving the string sep between sequence elements.
For example (continuing in the Shell from the previous section, using seq):
>>> ' '.join(seq)
'Go: Tear some strings apart!'
>>> ''.join(seq)
'Go:Tearsomestringsapart!'
>>> '//'.join(seq)
'Go://Tear//some//strings//apart!'
Predict and try each line, continuing in the Shell:
'##'.join(seq)
':'.join(['one', 'two', 'three'])
The methods split and join are often used in sequence:
Exercise 2.1.6.1. * Write a program underscores.py that would input a phrase from the user and print out the phrase with the white space between words replaced by an underscore. For instance if the input is "the best one", then it would print "the_best_one". The conversion can be done in one or two statements using the recent string methods.
Exercise 2.1.6.2. ** An acronym is a string of capital letters formed by taking the first letters from a phrase. For example, SADD is an acronym for 'students against drunk driving'. Note that the acronym should be composed of all capital letters even if the original words are not. Write a program acronym.py that has the user input a phrase and then prints the corresponding acronym.
To get you started, here are some things you will need to do. First check that you understand the basic syntax to accomplish the different individual tasks: Indicate the proper syntax using a Python function or operation will allow you to accomplish each task. Invent appropriate variable names for the different parts. This is not complete instructions! the idea is to make sure you know the basic syntax to use in all these situations. See the questions after the list to help you put together the final program.
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.)
As the dir('') list showed, there are many more operations on strings than we have discussed, and there are further variations of the ones above with more parameters. If you want to reach a systematic reference from inside Idle, go to Help-> Python Docs, select Library Reference, and Section 2.3 Built-in Types, and then Section 2.3.6.1, String Methods. (This depends on you being attached to the Internet, or having idle configured to look at a local copy of the official Python documentation.) Many methods use features we have not discussed yet, but currently accessible methods are capitalize, title, strip, rfind, replace....
The classes and methods introduced here are all used for the revised mad lib program developed in the next section.
Before making a version of the madlib program that is much easier to use with new stories, we need a couple of facts about other types of objects that are built into Python.
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 end. Lists have the method append. It modifies the original list. Another word for modifiable is mutable. Lists are mutable. Most of the types of object considered so far (int, str, float, tuple) are immutable or not mutable. Read and see how the list named words changes:
>>> words = list()
>>> words
[]
>>> words.append('animal')
>>> words
['animal']
>>> words.append('food')
>>> words
['animal', 'food']
>>> words.append('city')
>>> words
['animal', 'food', 'city']
This is particularly useful in a loop, where we can accumulate a new list. Read the start of this simple example:
def multipleAll(numList, multiplier):
'''Return a new list containing all of the elements of numList,
each multiplied by multiplier. For example:
>>> print multipleAll([3, 1, 7], 5)
[15, 5, 35]
'''
# more to come
Clearly this will be repetitious. We will process each element of the list numList. A for-each loop with numList is
appropriate. Also we need to create more and more elements of the new list. The accumulation pattern will work
here, with a couple of wrinkles.
Test yourself : If we are going to accumulate a list. How do we initialize the list?
In earlier versions of the accumulation loop, we needed an assignment statement to change the object doing the accumulating, but now the method append modifies its list automatically, so we do not need an assignment statement. Read and try the example program multiply.py:
def multipleAll(numList, multiplier): #1
'''Return a new list containing all of the elements of numList,
each multiplied by multiplier. For example:
>>> print multipleAll([3, 1, 7], 5)
[15, 5, 35]
'''
newList = list() #2
for num in numList: #3
newList.append(num*multiplier) #4
return newList #5
print multipleAll([3, 1, 7], 5) #6
Make sure the result makes sense to you or follow the details of playing computer below.
Line | numList | multiplier | newList | num | comment |
1-5 | - | - | - | - | definition |
6 | - | - | - | - | call function |
1 | [3, 1, 7 | 5 | - | set formal parameters | |
2 | [3, 1, 7 | 5 | [] | ||
3 | [3, 1, 7 | 5 | [] | 3 | first in list |
4 | [3, 1, 7 | 5 | [15] | 3 | append 3*5 = 15 |
3 | [3, 1, 7 | 5 | [15] | 1 | next in list |
4 | [3, 1, 7 | 5 | [15, 5] | 1 | append 1*5 = 5 |
3 | [3, 1, 7 | 5 | [15, 5] | 7 | last in list |
4 | [3, 1, 7 | 5 | [15, 5, 35] | 7 | append 7*5 = 35 |
3 | [3, 1, 7 | 5 | [15, 5, 35] | 7 | done with list and loop |
5 | [3, 1, 7 | 5 | [15, 5, 35] | 7 | return [15, 5, 35] |
6 | - | - | - | - | print [15, 3, 35] |
Using a for-loop and append is a powerful and flexible way to derive a new list, but not the only way.4
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:
>>> numberList = [2, 1, 3, 2, 5, 5, 2]
>>> aSet = set(numberList)
>>> aSet
set([1, 2, 3, 5])
Like other collections, a set can be used as a sequence in a for-loop. Read, and check it makes sense:
>>> for item in aSet:
print item
1
2
3
5
Predict the result of the following, and then paste it into the Shell and test. (Technically, a set is unordered, so you may not guess Python's order, but see if you can get the right length and the right elements in some order.)
set(['animal', 'food', 'animal', 'food', 'food', 'city'])
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.
The versions so far of the Mad Lib program have been fairly easy to edit to contain a different mad lib:
The first is a creative process. The second is a pretty mechanical process of looking at the story string and copying out the embedded cues. The first is best left to humans. The second can be turned over to a Python function to do automatically, as many times as we like, with any story – if we write it once.
Writing the Python code also takes a different sort of creativity! We shall illustrate a creative process. This is a bigger problem than any we have taken on so far. It is hard to illustrate a creative process if the overall problem is too simple.
Try and follow along. Read the sample code and pseudocode.
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.
First: how many times do we want to pull out a key – once for each embedded format. So how do we count those?
The count method is obviously a way to count. However we must count a fixed string, and the whole embedded formats vary (with different keys in the middle. A common part is '%(', and this should not appear in the regular text of the story, so it will serve our purpose:
repetitions = formatString.count('%(')
for i in range(repetitions):
...
This is certainly the most challenging code to date. Before jumping into writing it all precisely, we can give an overall plan in pseudo-code. For a plan we need an idea of what quantities we are keeping track of, and name them, and outline the sequence of operations with them.
Think about data to name:
In this case we are trying to find a list. We will need to extract one element at a time and add it to the list, so we need a list, say keyList.
The central task is to identifying the individual keys. When we find a key we can call it key.
Think about identifying the text of individual keys. This may be too hard to think of in the abstract, so let us use as a concrete example, and let us keep it simple for the moment. Suppose the data in formatString starts off as follows. The lines with numbers are added to help us refer to the indices. Display of possible data:
# 1111111111222222222233333333
# 01234567890123456789012345678901234567
'blah %(animal)s blah blah %(food)s ...'
The first key is 'animal' at formatString[7:13]. The next key is 'food' at formatString[28:32]. To identify each key as part of formatString we need not only the variable formatString, but also index variables to locate the start and end of the slices. Obvious names for the indices are start and end. We want to keep them current so the next key slice will always be
key = formatString[start : end]
Let us now put this all in an overall plan. We will have to continuously modify the start and end indices, the key, and the list. We have a basic pattern for accumulating a list, involving initializing it and appending to it. We can organize a plan, partly fleshed out, with a couple of approximations to be worked out still. 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 7. How in general would we locate the beginning of the slice we want?
We do not want the position of the beginning of '%(', but the position just after the '%('. Since the length of '%(' is 2, the correct position is 5+2 = 7. We can generalize this to
start = formatString.find('%(') + 2
OK, what about end? Clearly it is at the ')'. In this example,
formatString.find(')')
gives us 13, 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: In general there could be a parenthesized expression in the story before the key. Then the expression above would find the end of the earlier parenthesized expression, not the right one. 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('%(') + 2
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?
The search for '%(' will again start from the beginning of the format string, and will find the first key again. So what code will work for the second search? We search for the start of the next key going from the end of the last one:
start = formatString.find('%(', end) + 2
end = formatString.find(')', start)
This code will also work for later times through the loop: each time uses the end from the previous time through the loop.
So now what do we do for finding the first key? We could separate the treatment of the first key from all the others, but an easier approach would be to see if we can use the same code that already works for the later repetitions, and initialize variables right to make it work. If we are to find the first key with
start = formatString.find('%(', end) + 2
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, 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) + 2
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) + 2
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)s. This %(animal)s
liked to eat %(food)s, but the jungle had
very little %(food)s to offer. One day, an
explorer found the %(animal)s and discovered
it liked %(food)s. The explorer took the
%(animal)s back to %(city)s, where it could
eat as much %(food)s as it wanted. However,
the %(animal)s became homesick, so the
explorer brought it back to the jungle,
leaving a large supply of %(food)s.
The End
"""
print getKeys(originalStory)
The functions should behave as advertised.
Look back on the process described to come up with the getKeys function. One way of approaching the creative process of coding this function was provided. There are many other results and approaches possible, but the discussion did illustrate a number of useful ideas which you might adapt to other problems, in different orders and proportions, that are summarized in the next section.
There is still an issue for use of getKeys in the mad lib program: the returned list has repetitions in it, that we do not want in the mad lib program. We can easily create a collection without repetitions, how?
One approach is to make a set from the list returned. A neater approach would be to just have the getKeys function return a set in the first place. We need to slightly change to getKeys' documentation string and the final return line. This will be included in a new version of the mad lib program, which makes it easy to substitute a new story. We will make the story's format string be a parameter to the central method, tellStory. We will also put the clearly identified step of filling the dictionary with the user's picks in a separate function. We will test tellStory with the original story. Note the changes included in madlib2.py and run:
"""
madlib2.py
Interactive display of a mad lib, which is provided as a Python format string,
with all the cues being dictionary formats, in the form %(cue)s.
In this version, the cues are extracted from the story automatically,
and the user is prompted for the replacements.
Original mad lib adapted from code of Kirby Urner
"""
def getKeys(formatString): # change: returns a set
'''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) + 2
end = formatString.find(')', start)
key = formatString[start : end]
keyList.append(key) # may add duplicates
return set(keyList) # removes duplicates: no duplicates in a set
def addPick(cue, dictionary): # from madlib.py
'''Prompt the user and add one cue to the dictionary.'''
prompt = "Enter a specific example for %s: " % cue
dictionary[cue] = raw_input(prompt)
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(story):
'''story is a format string with Python dictionary references embedded,
in the form %(cue)s. Prompt the user for the mad lib substitutions
and then print the resulting story with the substitutions.
'''
cues = getKeys(story)
userPicks = getUserPicks(cues)
print story % userPicks
def main():
originalStory = '''
Once upon a time, deep in an ancient jungle,
there lived a %(animal)s. This %(animal)s
liked to eat %(food)s, but the jungle had
very little %(food)s to offer. One day, an
explorer found the %(animal)s and discovered
it liked %(food)s. The explorer took the
%(animal)s back to %(city)s, where it could
eat as much %(food)s as it wanted. However,
the %(animal)s became homesick, so the
explorer brought it back to the jungle,
leaving a large supply of %(food)s.
The End
'''
tellStory(originalStory)
main()
Does the use of well-named functions make it easier to follow this code? Make sure you follow the flow of execution and data.
After Python file manipulation is introduced, in Exercise 2.5.0.3you can modify the program to work on a madlib format string chosen by the user and taken from a file.
Exercise 2.3.3.1. ** Rename the example file locationsStub.py to be locations.py, and complete the function printLocations, to print the index of each location in the string s where target is located. For example, printLocations('This is a dish', 'is') would go through the string 'This is a dish' looking for the index of places where 'is' appears, and would return [2, 5, 11]. Similarly printLocations('This is a dish', 'h') would return [1, 13]. The program stub already uses the string method count. You will need to add code using the more general form of find.
Graphics make programming more fun for many people. To fully introduce graphics would involve many ideas that would be a distraction now. This section introduces a simplified graphics module developed by John Zelle for use with his Python Programming book. My slight elaboration of his package is graphics.py in the example programs.
Repeated caution: In Windows XP or Vista, be sure to start Idle from the shortcut provided in the examples (in the same directory as graphics.py). Do not start by clicking on an existing file to get a context menu and choosing Open With Idle: The 'Open With Idle' allows you to edit, but then when you go to run your graphics program, it fails miserably and with no clear reason.
Make sure you have Idle started from inside your Python folder, and have graphics.py in the same folder, so the Python interpreter can find it.
Note: you will just be a user of the graphics.py code, so you do not need to understand the inner workings! It uses all sorts of features of Python that are way beyond these tutorials. There is no particular need to open graphics.py in the Idle editor.
You will definitely want to be a user of the graphical module.
In Idle, in the Shell, enter the following lines, one at a time and read the explanations:
from graphics import *
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 line above makes all the types of object of Zelle's module accessible, as if they were already defined like built-in types str or list.
Pause after you enter the opening parenthesis below:
win = GraphWin(
The Idle editor tries to help you by displaying a pop-up tool tip with the parameter names and sometimes a default value after an equal sign. The default value is used if you supply nothing. In this case we will use the default values, so you can just finish by entering the closing parenthesis, now, completing
win = GraphWin()
Look around on your screen, and possibly underneath other windows: There should be a new window labeled “Graphics Window”. Bring it to the top, and preferably drag it around to make it visible beside your Shell window. A GraphWin is a type of object from Zelle's graphics package that automatically displays a window when it is created. The assignment statement remembers the window object as win for future reference. (This will be our standard name for our graphics window object.) A small window, 200 by 200 pixels is created. A pixel is the smallest little square that can by displayed on your screen. Modern screen usually have more than 1000 pixels across the whole screen.
Again, pause after entering the opening parenthesis below, and see how Idle hints at the meaning of the parameters to create a Point object. Then complete the line as given below:
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 method5 draw. Enter
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.
Enter both of the lines of code
cir = Circle(pt, 25)
cir.draw(win)
The first line creates a Circle object with center at the previously defined pt and with radius 25. This object is remembered with the name cir. As with all graphics objects that may be drawn within a GraphWin, it is only made visible by explicitly using its draw method:
So far, everything has been drawn in the default color black. Graphics objects like a Circle have methods to change their colors. Basic color name strings are recognized. You can choose the color for the circle outline as well as filling in the inside. Enter both lines
cir.setOutline("red")
cir.setFill("blue")
Now add
line = Line(pt, Point(150, 100))
line.draw(win)
A Line object is constructed with two Points as parameters. In this case we use the previously named Point, pt, and specify another Point directly. Technically the Line object is a segment between the the two points.
A rectangle is also specified by two points. The points must be diagonally opposite corners. Try
rect = Rectangle(Point(20, 10), pt)
rect.draw(win)
You can move objects around in a GraphWin. This will be handy for animation, shortly. 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 enter:
line.move(10, 40)
Did you remember that the y coordinate increases down the screen?
Feel free to further modify the graphics window using the methods introduced. To do this in a more systematic and easily reproduced way, we will shortly switch to program files, but then you do not get to see the effect of each statement individually and immediately!
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:
win.close()
An addition I have made to Zelle's package is the ability to print a string value of graphics objects for debugging purposes. Assuming you downloaded graphics.py from the hands-on site (not Zelle's), continue in the Shell with
print line
If some graphics object isn't visible because it is underneath something else of off the screen, this sort of output might be a good reality check.
Here is a very simple program, face.py. The only interaction is to click the mouse to close the graphics window. Have a directory window open to the Python examples folder containing face.py. In Windows you can double click on the icon for face.py to run it.
After you have checked out the picture, click with the mouse inside the picture, as requested, to terminate the program.
After you have run the program, you can examine the program in Idle or look below. The whole program is shown first; smaller pieces of it are discussed later:
'''A simple graphics example constructs a face from basic shapes.
'''
from graphics import *
def main():
winWidth = 200 # give a name to the window width
winHeight = 150 # and height
win = GraphWin('Face', winWidth, winHeight) # give title and dimensions
win.setCoords(0, 0, winWidth, winHeight) # make right side up coordinates!
head = Circle(Point(40,100), 25) # set center and radius
head.setFill("yellow")
head.draw(win)
eye1 = Circle(Point(30, 105), 5)
eye1.setFill('blue')
eye1.draw(win)
eye2 = Line(Point(45, 105), Point(55, 105)) # set endpoints
eye2.setWidth(3)
eye2.draw(win)
mouth = Oval(Point(30, 90), Point(50, 85)) # set corners of bounding box
mouth.setFill("red")
mouth.draw(win)
message = Text(Point(winWidth/2, 20), 'Click anywhere to quit.')
message.draw(win)
win.getMouse()
win.close()
main()
Let us look at individual parts.
Until further notice the set-off code is for you to read and have explained.
Immediately after the documentation string, always have the import line in your graphics program, to allow easy access to the graphics.py module:
from graphics import *
Though not a graphics issue, the first two lines of the main method illustrate a very good practice:
winWidth = 200 # give a name to the window width
winHeight = 150 # and height
Important parameters for your programs should get names. Within the program the names will make more sense to the human user than the literal data values. Plus, in this program, these parameters are used several times. If I choose to change the window size to 400 by 350, I only need to change the value of each dimension in one place!
win = GraphWin('Face', winWidth, winHeight) # give title and dimensions
win.setCoords(0, 0, winWidth, winHeight) # make right side up coordinates!
The first line shows the more general parameters for constructing a new GraphWin, a window title and dimensions in pixels. The second line shows how to turn the coordinate system right-side-up, so the y coordinate increases up the screen. The setCoords method sets up a new coordinate system, where the first two numbers are the coordinates you wish to use for the lower left corner of the window, and the last two numbers are the coordinates of the upper right corner. Thereafter, all coordinates are given in the new coordinate system, and the graphics module silently calculates the correct underlying pixel positions. All the lines of code up to this point in the program are my standard graphics program starting lines (other than the specific values for the title and dimensions). You will likely start your programs with similar code.
head = Circle(Point(40,100), 25) # set center and radius
head.setFill("yellow")
head.draw(win)
eye1 = Circle(Point(30, 105), 5)
eye1.setFill('blue')
eye1.draw(win)
The lines above create two circles, in each case specifying the centers directly. They are filled in and made visible.
eye2 = Line(Point(45, 105), Point(55, 105)) # set endpoints
eye2.setWidth(3)
eye2.draw(win)
The code above draws and displays a line, and illustrates another method available to graphics object, setWidth, making a thicker line.
mouth = Oval(Point(30, 90), Point(50, 85)) # set corners of bounding box
mouth.setFill("red")
mouth.draw(win)
The code above illustrates another kind of graphics object, an Oval (or ellipse). There are several ways an oval could be specified. Zelle chose to have you specify the corners of the bounding box that is just as high and as wide as the oval. This rectangle is only imagined, not actually drawn. (If you want to see such a rectangle, create a Rectangle object.)
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 raw_input or input, it is important to prompt the user before waiting for a response! In a GraphWin, the prompt must be made with a Text object displayed explicitly before the response is expected. Lines like the following will often end a program that has a final displayed picture:
message = Text(Point(winWidth/2, 20), 'Click anywhere to quit.')
message.draw(win)
win.getMouse()
win.close()
The parameters to construct the Text object are the point at the center of the text, and the text string itself. See how the text position is set up to be centered from left to right, half way across the window's width. Also note, that because the earlier win.setCoord call put the coordinates in the normal orientation, the y coordinate, 20, is close to the bottom of the window.
After the first two lines draw the promping text, the line win.getMouse() waits for a mouse click. In this program, the position is not important. (In the next example the position of this mouse click will be used.) As you have seen before, win.close() closes the graphics window.
While our earlier text-based Python programs have automatically terminated after the last line finishes executing, that is not true for programs that create new windows: The graphics window must be explicitly closed. The win.close() is necessary.
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 four lines can be used to terminate the program.
Another simple drawing example is balloons.py. Feel free to run it and look at the code in Idle. Note that the steps for the creation of all three balloons are identical, except for the location of the center of each balloon, so a loop over a list of the centers makes sense.
The next example, triangle.py, illustrates similar starting and ending code. In addition it explicitly interacts with the user. Rather than the code specifying literal coordinates for all graphical objects, the program remembers the places where the user clicks the mouse, and uses them as the vertices of a triangle.
Return to the directory window for the Python examples. In Windows you can double click on the icon for triangle.py to run it.
While running the program, follow the prompts in the graphics window and click with the mouse as requested.
After you have run the program, you can examine the program in Idle or look below:
'''Program: triangle.py or triangle.pyw (best name for Windows)
Interactive graphics program to draw a triangle,
with prompts in a Text object and feedback via mouse clicks.
Illustrates all of the most common GraphWin methods, plus
some of the ways to change the appearance of the graphics.
'''
from graphics import *
def main():
winWidth = 300
winHeight = 300
win = GraphWin('Draw a Triangle', winWidth, winHeight)
win.setCoords(0, 0, winWidth, winHeight) # make right-side-up coordinates!
win.setBackground('yellow')
message = Text(Point(winWidth/2, 20), 'Click on three points')
message.draw(win)
# Get and draw three vertices of triangle
p1 = win.getMouse()
p1.draw(win)
p2 = win.getMouse()
p2.draw(win)
p3 = win.getMouse()
p3.draw(win)
vertices = [p1, p2, p3]
triangle = Polygon(vertices)
triangle.setFill('gray')
triangle.setOutline('cyan')
triangle.setWidth(4) # width of boundary line
triangle.draw(win)
# Wait for a final click to exit
message.setText('Click anywhere to quit.')
message.setTextColor('red')
message.setStyle('italic')
message.setSize(20)
win.getMouse()
win.close()
main()
Let us look at individual parts.
Until further notice the set-off code is for you to read and have explained.
The lines before
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. The line above illustrates the last new common method of a GraphWin.
message = Text(Point(winWidth/2, 20), 'Click on three points')
message.draw(win)
Again a Text object is created. We will see below how the Text object can be modified later. This is the prompt for user action, expected for use in the lines
# Get and draw three vertices of triangle
p1 = win.getMouse()
p1.draw(win)
p2 = win.getMouse()
p2.draw(win)
p3 = win.getMouse()
p3.draw(win)
The win.getMouse() method (with no parameters), waits for you to click the mouse inside win. Then the Point where the mouse was clicked is returned. In this code three mouse clicks are waited for, remembered in variables p1, p2, and p3, and the points are drawn.
Next we introduce a very versatile type of graphical object, a Polygon, which may have any number of vertices specified in a list as its parameter. We see that the methods setFill and setOutline that we used earlier on a Circle, and the setWidth method available for a Line, also apply to a Polygon, (and also to other graphics objects).
vertices = [p1, p2, p3]
triangle = Polygon(vertices)
triangle.setFill('gray')
triangle.setOutline('cyan')
triangle.setWidth(4)
triangle.draw(win)
The next few lines illustrate most of the ways a Text object may be modified. Not only may the text string be changed. The appearance may be changed like in most word processors. The reference pages for graphics.py give the details.
# Wait for a final click to exit
message.setText('Click anywhere to quit.')
message.setTextColor('red')
message.setStyle('italic')
message.setSize(20)
After this prompt (with its artificially elaborate styling), we have the standard finishing lines:
win.getMouse()
win.close()
This Windows-specific section is not essential. It does describe how to make some Windows graphical programs run with less clutter.
If you ran the triangle.py program by double clicking its icon under Windows, you might have noticed a console window first appearing, followed by the graphics window. For this program, there was no keyboard input or screen output through the console window, so the console window was unused and unnecessary. In such cases, under Windows, you can change the source file extension from .py to .pyw, suppressing the display of the console window. If you are using windows, check it out.
The distinction is irrelevant inside Idle, which always has its Shell window.
This optional section only looks forward to more elaborate graphics systems than are used in this tutorial.
One limitation of the graphics.py module is that it is not robust if a graphics window is closed by clicking on the standard operating system close button on the title bar. If you close a graphics window that way, you are likely to get a Python error message. On the other hand, if your program creates a graphics window and then terminates abnormally due to some other error, the graphics window may be left orphaned. In this case the close button on the title bar is important: it is the easiest method to clean up and get rid of the window!
This lack of robustness is tied to the simplification designed into the graphics module. Modern graphics environments are event driven. The program can be interrupted by input from many sources including mouse clicks and key presses. This style of programming has a considerable learning curve. In Zelle's graphics package, the complexities of the event driven model are pretty well hidden. If the programmer wants user input, only one type can be specified at a time (either a mouse click in the graphics window via the getMouse method, or via the raw_input or input keyboard entry methods into the 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.
Meanwhile you can look at http://mcsp.wartburg.edu/zelle/python/graphics/graphics/index.html. It is important to pay attention to the organization of the reference: Most graphics object share a number of common methods. Those methods are described together, first. Then, under the headings for specific types, only the specialized additional methods are discussed.
Exercise 2.4.5.1. * Make a program scene.py creating a scene with the graphics methods. You are likely to need to adjust the positions of objects by trial and error until you get the positions you want. Make sure you have graphics.py in the same directory as your program.
Exercise 2.4.5.2. * Elaborate your scene program so it becomes changeScene.py, and changes one or more times when you click the mouse (and use win.getMouse()). You may use the position of the mouse click to affect the result, or it may just indicate you are ready to go on to the next view.
Zelle chose to have the constructor for a Rectangle take diagonally opposite corner points as parameters. Suppose you prefer to specify only one corner and also specify the width and height of the rectangle. You might come up with the following function, makeRect, to return such a new Rectangle. Read the following attempt:
def makeRect(corner, width, height):
'''Return a new Rectangle given one corner Point and the dimensions.'''
corner2 = corner
corner2.move(width, height)
return Rectangle(corner, corner2)
The second corner must be created to use in the Rectangle constructor, and it is done above in two steps. Start corner2 from the given corner and shift it by the dimensions of the Rectangle to the other corner. With both corners specified, you can use Zelle's version of the Rectangle constructor.
Unfortunately this is an incorrect argument. Run the example program makeRectBad.py:
'''Program: makeRectBad.py
Attempt a function makeRect (incorrectly), which takes
a takes a corner Point and dimensions to construct a Rectangle.
'''
from graphics import *
def makeRect(corner, width, height): # Incorrect!
'''Return a new Rectangle given one corner Point and the dimensions.'''
corner2 = corner
corner2.move(width, height)
return Rectangle(corner, corner2)
def main():
winWidth = 300
winHeight = 300
win = GraphWin('Draw a Rectangle (NOT!)', winWidth, winHeight)
win.setCoords(0, 0, winWidth, winHeight)
rect = makeRect(Point(20, 50), 250, 200)
rect.draw(win)
# Wait for another click to exit
msg = Text(Point(winWidth/2, 20),'Click anywhere to quit.')
msg.draw(win)
win.getMouse()
win.close()
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 Rectangles, where the opposite corners are at the same point! Hm, well the program did make the corners be the same initially. Recall we set
corner2 = corner
What happens after that?
Read and follow the details of what happens.
We need to take a much more careful look at what naming an object means. A good way to visualize this association between a name and an object is to draw an arrow from the name to the object associated with it. The object here is a Point, which has an x and y coordinate describing its state, so when the makeRect method is started the parameter name corner is associated with the actual parameter, a Point with coordinates (20, 50).
Next, the assignment statement associates the name corner2 with the same object. It is another name, or alias, for the original Point.
The next line,
corner2.move(width, height)
internally changes or mutates the Point object, and since in this case width is 250 and height is 200, the coordinates of the Point associated with the name corner2 change to 20+250=270 and 50+200=250:
Look! The name corner is still associated with the same object, but that object has changed internally! That is the problem: we wanted to keep the name corner associated with the point with original coordinates, but it has been modified.
The solution is to use the clone method that is defined for all the graphical objects in graphics.py. It creates a separate object, which is a copy with an equivalent state. We just need to change the line
corner2 = corner
to
corner2 = corner.clone()
A diagram of the situation after the cloning is:
Though corner and corner2 refer to points with equivalent coordinates, they do not refer to the same object. Then after
corner2.move(width, height)
we get:
No conflict: corner and corner2 refer to the corners we want. Run the corrected example program, makeRectange.py.
Read this section if you want a deeper understanding of the significance of muable and immutable objects.
This alias problem only came up because a Point is mutable. We had no such problems with the immutable types int or str.
Read and follow the discussion of the following code.
Just for comparison, consider the corresponding diagrams for code with ints that looks superficially similar:
a = 2
b = a
b = b + 3
After the first two lines we have an alias again:
The third line does not change the int object 2. The result of the addition operation refers to a different object, 5, and the name b is assigned to it:
Hence a is still associated with the integer 2 – no conflict.
It is not technically correct to think of b as being the number 2, and then 5, but a little sloppiness of thought does not get you in trouble with immutable types. With mutable types, however, be very careful of aliases. Then it is very important to remember the indirectness: that a name is not the same thing as the object it refers to.
Another mutable type is list. A list can be cloned with the slice notation: [:]. Try the following in the Shell:6
nums1 = [1, 2, 3]
nums2 = nums1[:]
nums2.append(4)
nums1
nums2
Run the example program, backAndForth0.py. The whole program is shown below for convenience. Then each individual new part of the code is discussed individually:
'''Test animation and depth.
'''
from graphics import *
import time
def main():
winWidth = 300
winHeight = 300
win = GraphWin('Back and Forth', winWidth, winHeight)
win.setCoords(0, 0, winWidth, winHeight)
rect = Rectangle(Point(200, 90), Point(220, 100))
rect.setFill("blue")
rect.draw(win)
cir1 = Circle(Point(40,100), 25)
cir1.setFill("yellow")
cir1.draw(win)
cir2 = Circle(Point(150,125), 25)
cir2.setFill("red")
cir2.draw(win)
for i in range(46): # animate cir1 to the right
cir1.move(5, 0)
time.sleep(.05)
for i in range(46): # animate cir1 to the left
cir1.move(-5, 0)
time.sleep(.05)
# Wait for a final click to exit
Text(Point(winWidth/2, 20), 'Click anywhere to quit.').draw(win)
win.getMouse()
win.close()
main()
Read the discussion below of pieces of the code from the program above. Do not try to execute fragments alone.
There is a new form of import statement:
from graphics import *
import time
The program uses a function from the time module. The syntax used for the time module is actually the safer and more typical way to import a module. As you will see later in the program, the sleep function used from the time module will be referenced as time.sleep(). This tells the Python interpreter to look in the time module for the sleep function.
If we had used the import statement
from time import *
then the sleep function could just be referenced with sleep(). This is obviously easier, but it obscures the fact that the sleep function is not a part of the current module. Also several modules that a program imports might have functions with the same name. With the individual module name prefix, there is no ambiguity. Hence the form import moduleName is actually safer than from moduleName import *.
You might think that all modules could avoid using any of the same function names with a bit of planning. To get an idea of the magnitude of the issue, have a look at the number of modules available to Python. Try the following in the in the Shell (and likely wait a number of seconds):
help('modules')
Without module names to separate things out, it would be very hard to totally avoid name collisions with the enormous number of modules you see displayed, that are all available to Python!
Back to the current example program: The main program starts with standard window creation, and then makes three objects:
rect = Rectangle(Point(200, 90), Point(220, 100))
rect.setFill("blue")
rect.draw(win)
cir1 = Circle(Point(40,100), 25)
cir1.setFill("yellow")
cir1.draw(win)
cir2 = Circle(Point(150,125), 25)
cir2.setFill("red")
cir2.draw(win)
Zelle's reference pages do not mention the fact that the order in which these object are first drawn is significant. If objects overlap, the ones which used the draw method later appear on top. Other object methods like setFill or move do not alter which are in front of which. This becomes significant when cir1 moves. The moving cir1 goes over the rectangle and behind cir2. (Run the program again if you missed that.)
The animation starts with the code for a simple repeat loop:
for i in range(46): # animate cir1 to the right
cir1.move(5, 0)
time.sleep(.05)
This very simple loop animates cir1 moving in a straight line to the right. As in a movie, the illusion of continuous motion is given by jumping only a short distance each time (increasing the horizontal coordinate by 5). The time.sleep function, mentioned earlier, takes as parameter a time in seconds to have the program sleep, or delay, before continuing with the iteration of the loop. This delay is important, because modern computers are so fast, that the intermediate motion would be invisible without the delay. The delay can be given as a decimal, to allow the time to be a fraction of a second.
The next three lines are almost identical to the previous lines, and move the circle to the left (-5 in the horizontal coordinate each time).
for i in range(46): # animate cir1 to the left
cir1.move(-5, 0)
time.sleep(.05)
The window closing lines of this program include a slight shortcut from earlier versions.
Text(Point(winWidth/2, 20), 'Click anywhere to quit.').draw(win)
The text object used to display the final message only needs to be referred to once, so a variable name is not necessary: The result of the Text object returned by the constructor is immediately used to draw the object. If the program needed to refer to this object again, this approach would not work.
The next example program, backAndForth1.py, it just a slight variation, looking to the user just like the last version. Only the small changes are shown below. This version was written after noticing how similar the two animation loops are, suggesting an improvement to the program: Animating any object to move in a straight line is a logical abstraction well expressed via a function.
The loop in the initial version of the program contained a number of arbitrarily chosen constants, which make sense to turn into parameters. Also, the object to be animated does not need to be cir1, it can be any of the drawable objects in the graphics package. The name shape is used to make this a parameter:
def moveOnLine(shape, dx, dy, repetitions, delay):
for i in range(repetitions):
shape.move(dx, dy)
time.sleep(delay)
Then in the main function the two similar animation loops are reduced to a line for each direction:
moveOnLine(cir1, 5, 0, 46, .05)
moveOnLine(cir1, -5, 0, 46, .05)
Make sure you see these two lines with function calls behave the same way as the two animation loops in the main program of the original version.
Run the next example version, backAndForth2.py. The changes are more substantial here, and the display of the whole program is followed by display and discussion of the individual changes:
'''Test animation of a group of objects making a face.
'''
from graphics import *
import time
def moveAll(shapeList, dx, dy):
''' Move all shapes in shapeList by (dx, dy).'''
for shape in shapeList:
shape.move(dx, dy)
def moveAllOnLine(shapeList, dx, dy, repetitions, delay):
'''Animate the shapes in shapeList along a line.
Move by (dx, dy) each time.
Repeat the specified number of repetitions.
Have the specified delay (in seconds) after each repeat.
'''
for i in range(repetitions):
moveAll(shapeList, dx, dy)
time.sleep(delay)
def main():
winWidth = 300
winHeight = 300
win = GraphWin('Back and Forth', winWidth, winHeight)
win.setCoords(0, 0, winWidth, winHeight) # make right side up coordinates!
rect = Rectangle(Point(200, 90), Point(220, 100))
rect.setFill("blue")
rect.draw(win)
head = Circle(Point(40,100), 25)
head.setFill("yellow")
head.draw(win)
eye1 = Circle(Point(30, 105), 5)
eye1.setFill('blue')
eye1.draw(win)
eye2 = Line(Point(45, 105), Point(55, 105))
eye2.setWidth(3)
eye2.draw(win)
mouth = Oval(Point(30, 90), Point(50, 85))
mouth.setFill("red")
mouth.draw(win)
faceList = [head, eye1, eye2, mouth]
cir2 = Circle(Point(150,125), 25)
cir2.setFill("red")
cir2.draw(win)
moveAllOnLine(faceList, 5, 0, 46, .05)
moveAllOnLine(faceList, -5, 0, 46, .05)
Text(Point(winWidth/2, 20), 'Click anywhere to quit.').draw(win)
win.getMouse()
win.close()
main()
Read the following discussion of program parts.
Moving a single elementary shape is rather limiting. It is much more interesting to compose a more complicated combination, like the face from the earlier example face.py. To animate such a combination, you cannot use the old moveOnLine function, because we want all the parts to move together, not one eye all the way across the screen and then have the other eye catch up! A variation on moveOnLine is needed where all the parts move together. We need all the parts of the face to move one step, sleep, and all move again, .... This could all be coded in a single method, but there are really two ideas here:
This suggests two functions. Another issue is how to handle a group of elementary graphics objects. The most basic combination of objects in Python is a list, so we assume a parameter shapeList, which is a list of elementary graphics objects. For the first function, moveAll, just move all the objects in the list one step. Since we assume a list of objects and we want to move each, this suggests a for-each loop:
def moveAll(shapeList, dx, dy):
''' Move all shapes in shapeList by (dx, dy).'''
for shape in shapeList:
shape.move(dx, dy)
Having this function, we can easily write the second function moveAllOnLine, with a simple change from the moveOnLine function, substituting the moveAll function for the line with the move method:
def moveAllOnLine(shapeList, dx, dy, repetitions, delay):
'''Animate the shapes in shapeList along a line.
Move by (dx, dy) each time.
Repeat the specified number of repetitions.
Have the specified delay (in seconds) after each repeat.
'''
for i in range(repetitions):
moveAll(shapeList, dx, dy)
time.sleep(delay)
The code in main to construct the face is the same as in the earlier example face.py. Once all the pieces are constructed and colored, they must be placed in a list, for use in moveAllOnLine:
faceList = [head, eye1, eye2, mouth]
Then, later, the animation uses the faceList to make the face go back and forth:
moveAllOnLine(faceList, 5, 0, 46, .05)
moveAllOnLine(faceList, -5, 0, 46, .05)
This version of the program has encapsulated and generalized the moving and animating by creating functions and adding parameters that can be substituted. Again, make sure you see how the functions communicate to make the whole program work. This is an important and non-trivial use of functions.
Run the example program backAndForth3.py.
The final version, backAndForth3.py, uses the observation that the code to make a face embodies one unified idea, suggesting encapsulation inside a function. Once you have encapsulated the code to make a face, we can make several faces! Then the problem with the original code for the face is that all the positions for the facial elements are hard-coded: The face can only be drawn in one position. The full listing of backAndForth3.py below includes a makeFace function with a parameter for the position of the center of the face.
Beneath the listing of the whole program is a discussion of the individual changes:
'''Test animation of a group of objects making a face.
Combine the face elements in a function, and use it twice.
Have an extra level of repetition in the animation.
'''
from graphics import *
import time
def moveAll(shapeList, dx, dy):
''' Move all shapes in shapeList by (dx, dy).'''
for shape in shapeList:
shape.move(dx, dy)
def moveAllOnLine(shapeList, dx, dy, repetitions, delay):
'''Animate the shapes in shapeList along a line.
Move by (dx, dy) each time.
Repeat the specified number of repetitions.
Have the specified delay (in seconds) after each repeat.
'''
for i in range(repetitions):
moveAll(shapeList, dx, dy)
time.sleep(delay)
def makeFace(center, win): #NEW
'''display face centered at center in window win.
Return a list of the shapes in the face.
'''
head = Circle(center, 25)
head.setFill("yellow")
head.draw(win)
eye1Center = center.clone() # face positions are relative to the center
eye1Center.move(-10, 5) # locate further points in relation to others
eye1 = Circle(eye1Center, 5)
eye1.setFill('blue')
eye1.draw(win)
eye2End1 = eye1Center.clone()
eye2End1.move(15, 0)
eye2End2 = eye2End1.clone()
eye2End2.move(10, 0)
eye2 = Line(eye2End1, eye2End2)
eye2.setWidth(3)
eye2.draw(win)
mouthCorner1 = center.clone()
mouthCorner1.move(-10, -10)
mouthCorner2 = mouthCorner1.clone()
mouthCorner2.move(20, -5)
mouth = Oval(mouthCorner1, mouthCorner2)
mouth.setFill("red")
mouth.draw(win)
return [head, eye1, eye2, mouth]
def main():
winWidth = 300
winHeight = 300
win = GraphWin('Back and Forth', winWidth, winHeight)
win.setCoords(0, 0, winWidth, winHeight) # make right side up coordinates!
rect = Rectangle(Point(200, 90), Point(220, 100))
rect.setFill("blue")
rect.draw(win)
faceList = makeFace(Point(40, 100), win) #NEW
faceList2 = makeFace(Point(150,125), win) #NEW
stepsAcross = 46 #NEW section
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)
Text(Point(winWidth/2, 20), 'Click anywhere to quit.').draw(win)
win.getMouse()
win.close()
main()
Read the following discussion of program parts.
As mentioned above, the face construction function allows a parameter to specify where the center of the face is. The other parameter is the GraphWin that will contain the face.
def makeFace(center, win):
then the head is easily drawn, using this center, rather than cir1 with specific center point (40, 100):
head = Circle(center, 25)
head.setFill("yellow")
head.draw(win)
For the remaining Points 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 makeRectange function in Section 2.4.6. A clone of the original center Point is made, and then moved by the difference in the positions of the originally specified Points. For instance, in the original face, the center of the head and first eye were at (40, 110) and (30, 115). That means a shift between the two coordinates of (-10, 5), since 30-40 = -10 and 130-110 = 20.
eye1Center = center.clone() # face positions are relative to the center
eye1Center.move(-10, 5) # locate further points in relation to others
eye1 = Circle(eye1Center, 5)
eye1.setFill('blue')
eye1.draw(win)
The only other changes to the face are similar, cloning and moving Points, rather than specifying them with explicit coordinates.
eye2End1 = eye1Center.clone()
eye2End1.move(15, 0)
eye2End2 = eye2End1.clone()
eye2End2.move(10, 0)
eye2 = Line(eye2End1, eye2End2)
eye2.setWidth(3)
eye2.draw(win)
mouthCorner1 = center.clone()
mouthCorner1.move(-10, -10)
mouthCorner2 = mouthCorner1.clone()
mouthCorner2.move(20, -5)
mouth = Oval(mouthCorner1, mouthCorner2)
mouth.setFill("red")
mouth.draw(win)
Finally, the list of elements for the face must be returned to the caller:
return [head, eye1, eye2, mouth]
Then in the main function, the program creates a face in exactly the same place as before, but using the makeFace function, with the original center of the face Point(40, 100). Now with the makeFace function, with its center parameter, it is also easy to replace the old cir2 with a whole face!
faceList = makeFace(Point(40, 100), win)
faceList2 = makeFace(Point(150,125), win)
The animation section is considerably elaborated in this version.
stepsAcross = 46
dx = 5
dy = 3
wait = .01
for i in range(3):
moveAllOnLine(faceList, dx, 0, stepsAcross, wait)
moveAllOnLine(faceList, -dx, dy, stepsAcross/2, wait)
moveAllOnLine(faceList, -dx, -dy, stepsAcross/2, wait)
The unidentified numeric literals that were used before are replaced by named values that easily identify the meaning of each one. This also allows the numerical values to be stated only once, allowing easy modification.
The whole animation is repeated three times by the use of a simple repeat loop.
The animations in the loop body illustrate that the straight line of motion does not need to be horizontal. The second and third lines use a non-zero value of both dx and dy for the steps, and move diagonally.
Make sure you see now how the whole program works together, including all the parameters for the moves in the loop.
By the way, the documentation of the functions in a module you have just run in the Shell is directly available. Try in the Shell:
help(moveAll)
Exercise 2.4.8.1. ** Save backAndForth3.py to the new name backAndForth4.py. Add a triangular nose in the middle of the face in the makeFace function. Like the other features of the face, make sure the position of the nose is relative to the center parameter. Make sure the nose is included in the final list of elements of the face that get returned.
Exercise 2.4.8.2. ** Make a program faces.py that asks the user to click the mouse, and then draws a face at the point where the user clicked. Elaborate this with a simple repeat loop, so a face appears for each of 6 clicks.
Exercise 2.4.8.3. ** Animate two faces moving in different directions at the same time in a program move2Faces.py. You cannot use the moveAllOnLine function. You will have to make a variation of your own. You can use the moveAll function separately for each face. Hint: imagine the old way of making an animated cartoon. If each face was on a separate piece of paper, and you wanted to animate them moving together, you would place them separately, record one frame, move them each a bit toward each other, record another frame, move each another bit toward each other, record another frame, .... In our animations “record a frame” is replaced by a short sleep to make the position visible to the user. Make a loop to incorporate the repetition of the moves.
Read this section if you want to allow the user to enter text directly into a graphics window.
When using a graphics window, the shell window is still available. Keyboard input can be done in the normal text fashion, waiting for a response, and going on after the user presses the enter key. It is annoying to make a user pay attention to two windows, so the graphics module provides a way to enter text inside a graphics window, with the Entry type. The entry is a partial replacement for the raw_input function.
Run the simple example, greet.py, which is copied below:
"""Simple example with Entry objects.
Enter your name, click the mouse, and see greetings.
"""
from graphics import *
def main():
winWidth = 300
winHeight = 300
infoHeight = 15
win = GraphWin("Greeting", winWidth, winHeight)
win.setCoords(0,0, winWidth, winHeight)
instructions = Text(Point(winWidth/2, 40),
"Enter your name.\nThen click the mouse.")
instructions.draw(win)
entry1 = Entry(Point(winWidth/2, 200),10)
entry1.draw(win)
Text(Point(winWidth/2, 230),'Name:').draw(win) # label for the Entry
win.getMouse() # To know the user is finished with the text.
name = entry1.getText()
greeting1 = "Hello, %s!" % name
Text(Point(winWidth/3, 150), greeting1).draw(win)
greeting2 = "Bonjour, %s!" % name
Text(Point(2*winWidth/3, 100), greeting2).draw(win)
instructions.setText("Click anywhere to quit.")
win.getMouse()
win.close()
main()
The only part of this with new ideas is:
entry1 = Entry(Point(winWidth/2, 200),10)
entry1.draw(win)
Text(Point(winWidth/2, 230),'Name:').draw(win) # label for the Entry
win.getMouse() # To know the user is finished with the text.
name = entry1.getText()
The first line of this excerpt creates an Entry object, supplying its center point and a number of characters to leave space for (10 in this case).
As with other places where input is requested, a separate static label is added.
The way the underlying events are hidden in graphics.py, there is no signal when the user is done entering text in an Entry box. To signal the program, a mouse press is used above. In this case the location of the mouse press is not relevant, but once the mouse press is processed, execution can go on to reading the Entry text. The method name getText is the same as that used with a Text object.
Run the next example, addEntries.py, also copied below:
"""Example with two Entry objects and type conversion.
Do addition.
"""
from graphics import *
def main():
winWidth = 300
winHeight = 300
win = GraphWin("Addition", winWidth, winHeight)
win.setCoords(0,0, winWidth, winHeight)
instructions = Text(Point(winWidth/2, 30),
"Enter two numbers.\nThen click the mouse.")
instructions.draw(win)
entry1 = Entry(Point(winWidth/2, 250),25)
entry1.setText('0')
entry1.draw(win)
Text(Point(winWidth/2, 280),'First Number:').draw(win)
entry2 = Entry(Point(winWidth/2, 180),25)
entry2.setText('0')
entry2.draw(win)
Text(Point(winWidth/2, 210),'Second Number:').draw(win)
win.getMouse() # To know the user is finished with the text.
numStr1 = entry1.getText()
num1 = int(numStr1)
numStr2 = entry2.getText()
num2 = int(numStr2)
result = "The sum of\n%s\nplus\n%s\nis %s." % (num1, num2, num1+num2)
Text(Point(winWidth/2, 110), result).draw(win)
instructions.setText("Click anywhere to quit.")
win.getMouse()
win.close()
main()
There is not a separate graphical replacement for the input statement, so you only can read strings. With conversions, it is still possible to work with numbers.
Only one new graphical method has been included above:
entry1.setText('0')
Again the same method name is used as with a Text object. In this case I chose not to leave the Entry initially blank. The 0 value also reinforces that a numerical value is expected.
There is also an entry2 with almost identical code. After waiting for a mouse click, both entries are read, and the chosen names emphasizes they are strings. The strings must be converted to integers in order to do arithmetic and display the result.
The almost identical code for the two entries is a strong suggestion that this code could be written more easily with a function. You may look at the identically functioning example program addEntries2.py. The only changes are shown below. First there is a function to create an Entry and a centered static label over it.
def makeLabeledEntry(entryCenterPt, entryWidth, initialStr, labelText, win):
'''Return an Entry object with specified center, width in characters, and
initial string value. Also create a static label over it with
specified text. Draw everything in the GraphWin win.
'''
entry = Entry(entryCenterPt, entryWidth)
entry.setText(initialStr)
entry.draw(win)
labelCenter = entryCenterPt.clone()
labelCenter.move(0, 30)
Text(labelCenter,labelText).draw(win)
return entry
In case I want to make more Entries with labels later, and refer to this code again, I put some extra effort in, making things be parameters even if only one value is used in this program. The position of the label is made 30 units above the entry by using the clone and move methods. Only the Entry is returned, on the assumption that the label is static, and once it is drawn, I can forget about it.
Then the corresponding change in the main function is just two calls to this function:
entry1 = makeLabeledEntry(Point(winWidth/2, 250), 25,
'0', 'First Number:', win)
entry2 = makeLabeledEntry(Point(winWidth/2, 180), 25,
'0', 'Second Number:', win)
These lines illustrate that a statement may take more than one line. In particular, as in the Shell, Python is smart enough to realize that there must be a continuation line if the parentheses do not match.
While I was improving things, I also changed the conversions to integers. In the first version I wanted to emphasize the existence of both the string and integer data as a teaching point, but the num1Str and num2Str variables were only used once, so a more concise way to read and convert the values is to eliminate them:
num1 = int(entry1.getText())
num2 = int(entry2.getText())
Thus far we have only used common color names. In fact there are a very large number of allowed color names, and the abiltity to draw with custom colors.
First, the graphics package is built on an underlying graphics system, Tkinter, which has a large number of color names defined. Each of the names can be used by itself, like 'red', 'salmon' or 'aquamarine' or with a lower intensity by specifying with a trailing number 2, 3, or 4, like 'red4' for a dark red.
Though the ideas for the coding have not all been introduced, it is still informative to run the example program colors.py. As you click the mouse over and over, you see the names and appearances of a wide variety of built-in color names. The names must be place in quotes, but capitalization is ignored.
Custom colors can also be created. To do that requires some understanding of human eyes and color (and the Python tools). The only colors detected directly by the human eyes are red, green, and blue. Each amount is registered by a different kind of cone cell in the retina. As far as the eye is concerned, all the other colors we see are just combinations of these three colors. This fact is used in color video screens: they only directly display these three colors. A common scale to use in labeling the intensity of each of the basic colors (red, green, blue) is from 0 to 255, with 0 meaning none of the color, and 255 being the most intense. Hence a color can be described by a tuple of red, green, and blue intensities (often abbreviated RGB). The graphics package has a function, color_rgb, to create colors this way. For instance a color with about half the maximum red intensity, no green, and maximum blue intensity would be
aColor = color_rgb(128, 0, 255)
Such a creation can be used any place a color is used in the graphics, (i.e. circle.setFill(aColor) ).
Another interesting use of the color_rgb function is to create random colors. Run example program randomCircles.py. The code also is here:
"""Draw random circles.
"""
from graphics import *
import random, time
def main():
win = GraphWin("Random Circles", 300, 300)
for i in range(75):
r = random.randrange(256)
b = random.randrange(256)
g = random.randrange(256)
color = color_rgb(r, g, b)
radius = random.randrange(3, 40)
x = random.randrange(5, 295)
y = random.randrange(5, 295)
circle = Circle(Point(x,y), radius)
circle.setFill(color)
circle.draw(win)
time.sleep(.05)
Text(Point(150, 20), "Click to close.").draw(win)
win.getMouse()
win.close()
main()
Read the fragments of this program and their explanations:
To do random things, the program needs a function from the random module. This example shows that imported modules may be put in a comma separated list:
import random, time
You have already seen the built-in function range. To generate a list of all the integers 0, 1, ... 255, you would use
range(256)
This is the full list of possible values for the red, green or blue intensity parameter. For this program we randomly choose any one element from this list. Instead of the range function, use the random module's randrange function, as in
r = random.randrange(256)
b = random.randrange(256)
g = random.randrange(256)
color = color_rgb(r, g, b)
This gives randomly selected values to each of r, g, and b, which are then used to create the random color.
I want a random circle radius, but I do not want a number as small as 0, making it invisible. The range and randrange functions both refer to a possible list starting with 0 when a single parameter is used. It is also possible to add a different starting value as the first parameter. You still must specify a value past the end of the sequence. For instance
range(3, 40)
would refer to the list [3, 4, 5, ... , 39] (starting with 3 and not quite reaching 40). Similarly
random.randrange(3, 40)
randomly selects an arbitrary element of range(3, 40).
I use the two-parameter version to select random parameters for a Circle:
radius = random.randrange(3, 40)
x = random.randrange(5, 295)
y = random.randrange(5, 295)
circle = Circle(Point(x,y), radius)
What are the smallest and largest values I allow for x and y? 7
Random values are often useful in games.
Exercise 2.4.12.1. * Write a program ranges.py that uses the range function to produce the list [1, 2, 3, 4], and then print it. Also prompt the user for an integer n and print the list [1, 2, 3, ... , n] – including n. Hint: 8 Finally use a simple repeat loop to find and print five randomly chosen numbers from the range [1, 2, 3, ... , n] .
This section fits here logically (as an important built-in type of object) but it is not needed for the next chapter, Flow of Control, 3.
Thus far you have been able to save programs, but anything produced during the execution of a program has been lost when the program ends. Data has not persisted past the end of execution. Just as programs live on in files, you can generate and read data files in Python that persist after your program has finished running.
As far as Python is concerned, a file is just a string (often very large!) stored on your file system, that you can read or write gradually or all together.
Open a directory window for your Python program directory. First note that there is no file named sample.txt.
Make sure you have started Idle so the current directory is your Python program directory (for instance in Windows with the downloaded shortcut to Idle). Run the example program firstFile.py, shown below:
outFile = file('sample.txt', 'w')
outFile.write('My first output file!')
outFile.close()
The first line creates a file object, which links Python to your computer's file system. The first parameter in the file constructor gives the file name, sample.txt. The second parameter indicates how you use the file. The 'w' is short for write, so you will be creating and writing to a file (or if it already existed, destroying the old contents and starting over!). If you do not use any operating system directory separators in the name ('\' or '/' depending on your operating system), then the file will lie in the current directory. The assignment statement gives the python file object the name outFile.
The second line writes the specified string to the file.
The last line is important to clean up. Until this line, this Python program controls the file, and nothing may even be actually written to the file. (Since file operations are thousands of times slower than memory operations, Python buffers data, saving small amounts and writing all at once in larger chunks.) The close line is essential for Python to make sure everything is really written, and to relinquish control of the file. It is a common bug to write a program where you have the code to add all the data you want to a file, but the program does not end up creating a file. Usually this means you forgot to close the file.
Now switch focus and look at a file window for the current directory. You should now see a file sample.txt. You can open it in Idle (or your favorite word processor) and see its contents.
Run the example program nextFile.py, shown below, which has two calls to the write method:
outFile = file('sample2.txt', 'w')
outFile.write('My second output file!
outFile.write('Write some more.')
outFile.close()
Now look at the file, sample2.txt. Open it in Idle. It may not be what you expect! The write method for the file is not quite like a print statement. It does not add anything to the file except exactly the data you tell it to write. If you want a newline, you must indicate it explicitly. Recall the newline code \n. Run the example program revisedFile.py, shown below, which adds newline codes:
outFile = file('sample3.txt', 'w')
outFile.write('A revised output file!\n')
outFile.write('Write some more.\n')
outFile.close()
Check the contents of sample3.txt. This manner of checking the file shows it is really in the file system, but the focus in the Tutorial should be on using Python! Run the example program printFile.py, shown below:
inFile = file('sample3.txt', 'r')
contents = inFile.read()
print contents
Now you have come full circle: what one Python program has written into the file sample3.txt, another has read and displayed.
In the first line an operating system file (sample3.txt) is associated again with a Python variable name (inFile). The second parameter again gives the mode of operation, but this time it is 'r', short for read. This file, sample3.txt, should already exist, and the intention is to read from it. This is the most common mode for a file, so the 'r' parameter is actually optional.
The read method returns all the file's data as a single string, here assigned to contents. Using the close method is generally optional with files being read. There is nothing to lose if a program ends without closing a file that was being read.9
Exercise 2.5.0.2. Make the following programs in sequence. Be sure to save the programs in the same
directory as where you start the idle shortcut and where you have all the sample text files:
* a. printUpper.py: read the contents of the sample2.txt file and print the contents out in upper case. (This
should use file operations and should work no matter what the contents are in sample2.txt. Do not assume
the particular string written by nextFile.py!)
* b. fileUpper.py: prompt the user for a file name, read and print the contents of the requested file in upper
case.
** c. copyFileUpper: modify fileUpper.py to write the upper case string to a new file rather than printing
it. Have the name of the new file be dynamically derived from the old name by prepending 'UPPER' to
the name. For example, if the user specified the file sample.txt (from above), the program would create
a file UPPERsample.txt, containing 'MY FIRST OUTPUT FILE!'. When the user specifies the file name
stuff.txt, the resulting file would be named UPPERstuff.txt.
Exercise 2.5.0.3. Write madlib3.py, a small modification of madlib2.py, that 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, unlike in madlib2.py, where the story is a literal string coded directly into the program called originalStory. The tellstory function and particularly the getKeys function were developed and described in detail in this tutorial, but for this exercise there is no need to follow their inner workings - you are just a user of the tellstory function. You do not need to mess with the code for the definition of tellStory or any of the earlier supporting functions. The original madlib string is already placed in a file jungle.txt, that is in this format as an example. With the Idle editor, write another madlib format string into a file myMadlib.txt. If you earlier created a file myMadlib.py, then you can easily extract the story from there. Test your program both with jungle.txt and your new madlib story file.
The same typographical conventions will be used as in the last summary in Section 1.15.
returns a file object for reading, where nameInFileSystem must be a string referring to an existing file. The file location is relative to the current directory.
'w') returns a file object for writing, where the string nameInFileSystem will be the name of the file. If it did not exist before, it is created. CAUTION: If it did exist before, all previous contents are erased. The file location is relative to the current directory.
is a file opened for reading, and outfile is a file opened for writing, then
returns the entire file contents of the file as a string.
closes the file in the operating system (generally not needed, unless the file is going to be modified later, while your program is still running).
writes the string to the file, with no extra newline.
closes the file in the operating system (important to make sure the whole file gets written and to allow other access to the file).
loop heading :
move all objects a small step in the proper direction
time.sleep(delay).
You have varied the normal forward sequence of operations with functions and for loops. To have full power over your programs, you need two more constructions that changing the flow of control: decisions choosing between alternatives (if statements), and more general loops that are not required to be controlled by the elements of a collection (while loops).
The statements introduced in this chapter will involve tests or conditions. More syntax for conditions will be introduced later, but for now consider simple arithmetic comparisons that directly translate from math into Python. Try each line separately in the Shell
2 < 5
3 > 7
x = 11
x > 10
2*x < x
type(True)
You see that conditions are either True or False (with no quotes). These are the only possible Boolean values (named after 19th century mathematician George Boole). In Python the name Boolean is shortened to the type bool. It is the type of the results of true-false tests.
Run this program, suitcase.py, http://cs.luc.edu/anh/python/hands-on/examples/suitcase.py. Try it at least twice, with inputs: 30 and then 55. As you an see, you get an extra result, depending on the input. The main code is:
weight = input('How many pounds does you suitcase weigh? ')
if weight > 50:
print 'There is a $25 charge for luggage that heavy.'
print 'Thank you for your business.'
The middle two line are an if-statement. It reads pretty much like English. If it is true that the weight is greater than 50, then print the statement about an extra charge. If it is not true that the weight is greater than 50, then don't do the indented part: skip printing the extra luggage charge. In any event, when you have finished with the if-statement (whether it actually does anything or not), go on to the next statement that is not indented under the if. In this case that is the statement printing “Thank you”.
The general Python syntax for a simple if statement is
if condition :
indentedStatementBlock
If the condition is true, then do the indented statements. If the condition is not true, then skip the indented statements.
Another fragment as an example:
if balance < 0:
transfer = -balance
backupAccount = backupAccount - transfer # take enough from the backup acct.
balance = balance + transfer
As with other kinds of statements with a heading and an indented block, the block can have more than one statement. The assumption in the example above is that if an account goes negative, it is brought back to 0 by transferring money from a backup account in several steps.
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.
Run the example program, clothes.py. Try it at least twice, with inputs: 50, 80. As you can see, you get different results, depending on the input. The main code of clothes.py is:
temperature = input('What is the temperature? ')
if temperature > 70:
print 'Wear shorts.'
else:
print 'Wear long pants.'
print 'Get some exercise outside.'
The middle four lines are an if-else statement. Again it is close to English, though you might say “otherwise” instead of “else” (but else is shorter!). There are two indented blocks: One, like in the simple if statement, comes right after the if heading and is executed when the condition in the if heading is true. In the if-else form this is followed by an else: line, followed by another indented block that is only executed when the original condition is false. In an if-else statement exactly one of two possible indented blocks is executed.
A line is also shown outdented next, about getting exercise. Since it is outdented, it is not a part of the if-else statement: It is always executed in the normal forward flow of statements, after the if-else statement (whichever block is selected).
The general Python syntax is
if condition :
indentedStatementBlockForTrueCondition
else:
indentedStatementBlockForFalseCondition
These statement blocks can have any number of statements, and can include about any kind of statement.
All the usual arithmetic comparisons may be made, but many do not use standard mathematical symbolism, mostly
for lack of proper keys on a standard keyboard.
Meaning | Math Symbol | Python Symbols |
Less than | < | < |
Greater than | > | > |
Less than or equal |