Professional Documents
Culture Documents
Python 1 v2
Python 1 v2
Data in Pytho n
String Representatio ns
Numbers in Pytho n
Pro gram 2: Printing Simple Pytho n Expressio ns
A Few Sample Expressio ns
Variable-Width Fields
A Simple Listing Pro gram
Check Yo u Out!
Quiz 1 Quiz 2 Pro ject 1
Lesso n 8 : Mo re Abo ut Lo o ping
Fun with the range() functio n.
Using the enumerate() functio n
A Mo re Co mplex While Lo o p Example
While Lo o ps and User Input Validatio n
Dicts and Lo o ps
A Mo re Co mplex Example
Lo o p This
Quiz 1 Quiz 2 Pro ject 1
Lesso n 9 : Re ading and Writ ing File s
Creating a New File
Writing to a File
Reading Files as Text
Appending to a File
Seeking to Arbitrary Po sitio ns
Mo re File Details
Creating a File-Based To -Do List
Reading Binary Data
Files fo r Miles
Quiz 1 Quiz 2 Pro ject 1
Lesso n 10 : Pyt ho n's Built -In Funct io ns
Party Fun with Built-In Functio ns
abs(x)
all(iterable)
any(iterable)
bo o l(x)
chr(i)
dict(arguments)
dir(arguments)
glo bals()
help(o bject)
len(s)
lo cals()
max(iterable)
min(iterable)
o rd(c)
po w(x, y[, z])
reversed(seq)
ro und(x[, n])
so rted(iterable)
sum(iterable)
zip(*iterables)
Fun with Built-In Functio ns
Quiz 1 Quiz 2 Pro ject 1
Lesso n 11: De f ining and Calling Yo ur Own Funct io ns
Explo ring Functio ns
Write Yo ur First Functio n
Parameters and Arguments
Parameters and Arguments
Returning Values
Multiple Return Values
Functio ns and Namespaces
Parameters That Receive Multiple Arguments
Putting It All To gether
A So lid Fo undatio n
Quiz 1 Quiz 2 Pro ject 1
Lesso n 12: T he Pyt ho n St andard Library
Increased Versatility
Namespaces
Pytho n Mo dules
Writing Mo dules to be Testable
Splitting Up Yo ur Pro grams
Other Ways to Impo rt a Mo dule
impo rt ... as
fro m ... impo rt ...
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Getting Started
Welco me to the O'Reilly Scho o l o f Techno lo gy's (OST) Int ro duct io n t o Pyt ho n co urse! We're happy yo u've cho sen to learn
Pytho n pro gramming with us.
Course Objectives
When yo u co mplete this co urse, yo u will be able to :
In this co urse, yo u will learn the basics o f pro gramming with Pytho n. Using the Co deRunner integrated learning enviro nment,
yo u'll learn abo ut expressio ns, variables, co nditio nals, lo o ps, lists, sets, dicts, functio ns, o bjects and exceptio ns.
Besides a bro wser and internet co nnectio n, all so ftware is pro vided o nline by the O'Reilly Scho o l o f Techno lo gy.
To learn a new skill o r techno lo gy, yo u have to experiment. The mo re yo u experiment, the mo re yo u learn. Our system
is designed to maximize experimentatio n and help yo u learn to learn a new skill.
We'll pro gram as much as po ssible to be sure that the principles sink in and stay with yo u.
Each time we discuss a new co ncept, yo u'll put it into co de and see what YOU can do with it. On o ccasio n we'll even
give yo u co de that do esn't wo rk, so yo u can see co mmo n mistakes and ho w to reco ver fro m them. Making mistakes
is actually ano ther go o d way to learn.
Abo ve all, we want to help yo u to learn to learn. We give yo u the to o ls to take co ntro l o f yo ur o wn learning experience.
When yo u co mplete an OST co urse, yo u kno w the subject matter, and yo u kno w ho w to expand yo ur kno wledge, so
yo u can handle changes like so ftware and o perating system updates.
T ype t he co de . Resist the temptatio n to cut and paste the example co de we give yo u. Typing the co de
actually gives yo u a feel fo r the pro gramming task. Then play aro und with the examples to find o ut what else
yo u can make them do , and to check yo ur understanding. It's highly unlikely yo u'll break anything by
experimentatio n. If yo u do break so mething, that's an indicatio n to us that we need to impro ve o ur system!
T ake yo ur t im e . Learning takes time. Rushing can have negative effects o n yo ur pro gress. Slo w do wn and
let yo ur brain abso rb the new info rmatio n tho ro ughly. Taking yo ur time helps to maintain a relaxed, po sitive
appro ach. It also gives yo u the chance to try new things and learn mo re than yo u o therwise wo uld if yo u
blew thro ugh all o f the co ursewo rk to o quickly.
Expe rim e nt . Wander fro m the path o ften and explo re the po ssibilities. We can't anticipate all o f yo ur
questio ns and ideas, so it's up to yo u to experiment and create o n yo ur o wn. Yo ur instructo r will help if yo u
go co mpletely o ff the rails.
Acce pt guidance , but do n't de pe nd o n it . Try to so lve pro blems o n yo ur o wn. Go ing fro m
misunderstanding to understanding is the best way to acquire a new skill. Part o f what yo u're learning is
pro blem so lving. Of co urse, yo u can always co ntact yo ur instructo r fo r hints when yo u need them.
Use all available re so urce s! In real-life pro blem-so lving, yo u aren't bo und by false limitatio ns; in OST
co urses, yo u are free to use any reso urces at yo ur dispo sal to so lve pro blems yo u enco unter: the Internet,
reference bo o ks, and o nline help are all fair game.
Have f un! Relax, keep practicing, and do n't be afraid to make mistakes! Yo ur instructo r will keep yo u at it
until yo u've mastered the skill. We want yo u to get that satisfied, "I'm so co o l! I did it!" feeling. And yo u'll have
so me pro jects to sho w o ff when yo u're do ne.
Lesson Format
We'll try o ut lo ts o f examples in each lesso n. We'll have yo u write co de, lo o k at co de, and edit existing co de. The co de
will be presented in bo xes that will indicate what needs to be do ne to the co de inside.
Whenever yo u see white bo xes like the o ne belo w, yo u'll type the co ntents into the edito r windo w to try the example
yo urself. The CODE TO TYPE bar o n to p o f the white bo x co ntains directio ns fo r yo u to fo llo w:
CODE TO TYPE:
White boxes like this contain code for you to try out (type into a file to run).
If you have already written some of the code, new code for you to add looks like this.
If we want you to remove existing code, the code to remove will look like this.
We may also include instructive comments that you don't need to type.
We may run pro grams and do so me o ther activities in a terminal sessio n in the o perating system o r o ther co mmand-
line enviro nment. These will be sho wn like this:
INTERACTIVE SESSION:
Co de and info rmatio n presented in a gray OBSERVE bo x is fo r yo u to inspect and absorb. This info rmatio n is o ften
co lo r-co ded, and fo llo wed by text explaining the co de in detail:
OBSERVE:
Gray "Observe" boxes like this contain information (usually code specifics) for you to
observe.
The paragraph(s) that fo llo w may pro vide additio n details o n inf o rm at io n that was highlighted in the Observe bo x.
We'll also set especially pertinent info rmatio n apart in "No te" bo xes:
Note No tes pro vide info rmatio n that is useful, but no t abso lutely necessary fo r perfo rming the tasks at hand.
T ip Tips pro vide info rmatio n that might help make the to o ls easier fo r yo u to use, such as sho rtcut keys.
WARNING Warnings pro vide info rmatio n that can help prevent pro gram crashes and data lo ss.
Co de Edito r Demo
Co ursewo rk Demo
The Pytho n language was created by Guido van Ro ssum in the late 19 8 0 s. It was intended to be simple to use and
easy to understand. The mo st interesting new feature o f the language was its use o f indentatio n to illustrate structure,
similar to the way we use indentatio n in o ur everyday pro se and written language.
Pytho n was built to have a small "co re," to keep it accessible, and a large library to make it versatile. Van Ro ssum was
interested in netwo rking. That interest pro mpted quick develo pment o f a useful set o f netwo rk libraries fo r the language;
many mo re libraries have been added since then.
To day, Pytho n is used just abo ut everywhere. Majo r users include Yo uTube, Go o gle, Yaho o !, CERN, and NASA, and
ITA—the co mpany that pro duces the ro ute search engine used by Orbitz, CheapTickets, travel agents—as well as
many do mestic and internatio nal airlines.
Pytho n is an interpreted language, which means Pytho n co de isn't translated into the binary instructio ns that
co mputers actually run. Instead, bytecode is created, and the interpreter uses that byteco de to tell it what to do . Pytho n
is also a dynamic language. This means that aspects o f yo ur pro gram which beco me fixed early o n in so me
languages, remain available fo r yo u to change in Pytho n, even while yo ur pro gram is running.
In this co urse, we'll be using the latest versio n o f Pytho n (3.1). Yo u may find that o ther peo ple are using o lder versio ns.
Fo rtunately, the differences between versio ns are mino r. We'll go o ver the changes yo u'll need to be aware o f in o rder
to wo rk with o lder versio ns as well.
In the next sectio n, we'll finally enter so me Pytho n co de and run it!
Programming in Python
A First Program
It is a traditio n when learning a new language in co mputer pro gramming to print the wo rds "hello wo rld" as a
first example. Pytho n can print "hello wo rld" in a single line o f co de, so that do esn't make fo r a great example
here. Instead, we'll lo o k at a slightly mo re co mplicated example that no t o nly prints "hello " and "go o dbye," but
also do es a little calculatio n alo ng the way.
Usually yo u'll enter a Pytho n pro gram in yo ur favo rite text edito r and then run it by typing a co mmand in a
co mmand shell (that's the term fo r o ne o f tho se special pro grams who se jo b in life is to display a pro mpt and
then let yo u type in a co mmand that executes when yo u press Enter). On a Unix o r Linux type o f system (that
includes OS X), the shell is bash, o r tcsh, o r so me o ther -sh, and the edito r is vim o r Emacs o r o ne o f many
o ther po ssibilities. On Windo ws, the shell is the DOS windo w (what yo u get by running "cmd") and the edito r
is No tepad o r Wo rdpad o r vim o r, again, any o f many o ther po ssibilities. (Yo u can also run pro grams o n
Windo ws by do uble-clicking their ico ns, but then the usual input and o utput streams are no t available.)
In this co urse we're go ing to use a co mpletely integrated enviro nment because we want the co urse to be
abo ut learning Pytho n, no t abo ut learning a particular edito r and o perating system. Later in the co urse, there
is a brief lesso n o n basic Unix co mmands and edito rs so that, when yo u're ready, yo u can transfer yo ur
kno wledge o f Pytho n to the real wo rld.
Let's get started o n yo ur first pro gram! First, we'll create a fo lder to keep all o f o ur Pytho n stuff o rganized. In
the left panel o f yo ur Co deRunner windo w, right-click Ho m e , and select Ne w f o lde r... as sho wn:
No w yo u sho uld be able to see yo ur pyt ho n1 fo lder listed in the File Bro wse r o n the left side o f yo ur
screen:
No w, in the edito r windo w area belo w this lesso n text, enter the co de belo w:
CODE TO TYPE:
print("Hello World")
print("I have", 3 + 4, "bananas")
print("Goodbye, World")
In the edito r menu bar, click the Save ico n: (we'll sho w that ico n fro m no w o n when we want yo u to save a
file). Select yo ur /pyt ho n1 fo lder and enter the file name he llo _wo rld.py:
To run the pro gram, o pen a Unix shell. Click the Ne w T e rm inal ico n at the to p o f the edito r windo w and,
when pro mpted, enter yo ur OST username and passwo rd (the passwo rd wo n't appear as yo u type it):
INTERACTIVE SESSION:
Once lo gged in, yo u'll see a co ld1:~$ pro mpt. Change to yo ur /pyt ho n1 fo lder and then run the pro gram as
sho wn:
INTERACTIVE SESSION:
cold1:~$ cd python1
cold1:~/python1$ python3 ./hello_world.py
Hello World
I have 7 bananas
Goodbye, World
cold1:~/python1$
Co ngratulatio ns! Yo u're o fficially a Pytho n pro grammer! Of co urse this pro gram isn't very co mplex, but the
interpreter has do ne the calculatio n yo u asked it to do . Pat yo urself o n the back! Yo u're o ff to a stro ng start.
Experiment with o ther calculatio ns.
OBSERVE:
cold1:~$ cd python1
cold1:~/python1$ python3 ./hello_world.py
First we used the Unix changed directo ry (cd) co mmand to change to the pyt ho n1 fo lder where o ur pro gram
is lo cated. Then we ran pyt ho n3, the current versio n o f the Pytho n interpreter, telling it to run the
he llo _wo rld.py pro gram lo cated in the current fo lder (./)
Note When yo u want to exit fro m the interactive sessio n, type e xit and press Ent e r, o r press Ct rl+d.
T he Interactive Interpreter
In Pytho n, yo u can run the interpreter in interactive mo de when yo u want to try things o ut and see results
printed right away. That instant feedback is really handy when yo u're learning a new language.
Co deRunner gives yo u access to interactive Pytho n sessio ns—click the New Terminal ico n, and in the
Terminal windo w, type yo ur OST lo gin and passwo rd (again, it do esn't appear while yo u're typing it), and then
type pyt ho n3:
INTERACTIVE SESSION:
The pro mpt >>> indicates that the interpreter is ready fo r yo ur input.
If yo u enter o ne o f the lines fro m the pro gram yo u just ran, the o utput will appear. This interactive interpreter
windo w allo ws yo u to enter bo th statements and expressio ns (we'll co ver tho se in detail later). Statements
are executed pretty much as if they were part o f a pro gram; the expressio ns are evaluated and the resulting
value is printed (as lo ng as yo u're in interactive mo de).
Type the co mmands belo w in the interpreter windo w (Remember: when we say TYPE the code, do it. It's good
for you!). The interpreter prints a result fo r each expressio n. (Yo u'll see a different pro mpt after the fo urth line.
We'll talk abo ut that in a minute):
INTERACTIVE SESSION:
So , what happened here? The first three lines are all examples o f string concatenation—a seco nd string is
appended to the first, giving a lo nger string as a result. Strings can have either single (') o r do uble (")
quo tatio n marks aro und them, and either o ne quo tatio n mark o r three at the beginning and end o f the string.
Use exactly the same co mbinatio n at bo th ends.
The last expressio n sho ws an impo rtant difference between the o ne-quo tatio n mark and the three-quo tatio n
mark fo rms. A string given in o ne-quo tatio n mark fo rm must begin and end o n the same line. Three-quo tatio n
mark strings can spread acro ss mo re than o ne line.
The fo urth example actually do es extend acro ss two lines, so the interpreter changed its pro mpt fro m >>> to
... (an ellipsis) after yo u entered the first line. The ellipsis lets yo u kno w that yo u've go t an inco mplete
statement o r expressio n, and the interpreter is waiting fo r yo u to finish it. When yo u co mpleted the string with
the seco nd line o f input, the interpreter then printed the value o f the two -line expressio n, and returned the
no rmal >>> pro mpt. Yo u can see that the line feed between he llo and wo rld is represented by \n, which is
kno wn in Pytho n as a string escape sequence.
Data in Python
In Pytho n there are vario us types o f data yo u can manipulate. The simplest are strings. There are also vario us numeric
data types: integers, flo ats, and co mplex numbers. Let's see ho w to write tho se values in yo ur pro grams.
String Representations
We've seen that Pytho n has several ways o f representing strings. Fo r regular strings, we use either o f the
o ne-quo tatio n mark fo rms. Use three-quo tatio n mark strings if, fo r example, the value yo u need to represent
co ntains newlines, o r co ntains quo tatio n marks itself. The interpreter represents certain characters using
escape sequences. Yo u can put escape sequences into yo ur strings to insert certain literal o r no n-printing
characters. Here's a list o f the mo st co mmo n sequences:
Yo u can build a really lo ng string using triple-quo tatio n mark strings and escaping the newlines, o r by placing
several different strings o ne after the o ther in yo ur so urce co de. Usually yo u'll extend tho se types o f
statements acro ss multiple lines using parentheses; the interpreter will assume a statement o r expressio n is
inco mplete if it runs into unmatched parentheses. Co ntinue the interpreter sessio n o r start a new o ne and try
these co mmands:
INTERACTIVE SESSION:
>>> """One\
... Two\
... Three"""
'OneTwoThree'
>>> ("One" "Two" "Three")
'OneTwoThree'
>>> 'OneTwoThree'
'OneTwoThree'
The interpreter sho uld print the same value back after yo u enter each o f the three strings. The first string yo u
entered spans three lines, but o nly printed o ut o ne.
Numbers in Python
In Pytho n, numbers are represented as yo u might expect. Integers are strings o f digits. The digits can be
preceded by a minus sign (-) fo r negative numbers. There is no limit o n integer values in Pytho n, altho ugh the
larger they get, the lo nger it takes yo u to do anything with them!
In Pytho n, yo u cannot use co mmas to separate gro ups o f digits like yo u so metimes do in text
Note do cuments.
A flo ating-po int number is made up o f an integer fo llo wed by a decimal po int and a fractio nal part. Yo u may
also use exponential notation if yo u like, by placing the letter E after the integer.
Co mplex numbers generally co nsist o f a real part and an imaginary part that's fo llo wed by a J ; the real part is
separated fro m the imaginary part by a plus o r minus sign. The imaginary number fo llo wed by the J can
co mprise a co mplex number in Pytho n as well. (Fo r yo u mathematicians wo ndering why i wasn't used, this is
standard engineering no tatio n. The rest o f us can just carry o n.)
Let's try so me o f this stuff o ut. If yo u do n't have an interpreter sessio n o pen, click the Ne w T e rm inal ico n and
type pyt ho n3 again. Then, try these numbers:
INTERACTIVE SESSION:
>>> 1
>>> -1000
>>> 12.34
>>> 1.234E2
>>> 1+2j
>>> 1j
Huh. it seems the interpreter do esn't always print a value the way yo u enter it. Flo ating po int numbers aren't
always exact, tho ugh the interpreter gets as clo se as po ssible. Altho ugh the erro rs are relatively small, yo u
want to keep them fro m accumulating to o much in lo ng strings o f calculatio ns. (Mo re o n that later.) If so me o f
this isn't quite clear to yo u yet, do n't wo rry. We're just getting started. We'll be talking abo ut it all lo ts mo re and
yo u'll have many chances to try things o ut and ask questio ns.
CODE TO TYPE:
print("""--------------------
Some Calculations
--------------------""")
print(314159E-5)
print(10**6, 1j**2)
print(3 + 2 * 4, 1 / 3)
print("-" * 20)
print((3.14159 * 16) ** 2)
print(3.14159 * 16 ** 2)
print(20 * "-")
print("--------------------\nEnd of Calculations\n--------------------")
Save it in yo ur /pyt ho n1 fo lder as calculat io ns.py and run it in the Terminal tab:
INTERACTIVE SESSION:
Take a minute to po nder. Think deeply and make sure yo u understand all o f yo ur results befo re go ing further.
Fo r example, yo u might wo nder, why does 3 + 2 * 4 give 11, and not 20? Hmm... so mething to think abo ut!
Feel free to talk with yo ur instructo r if yo u are in any way befuddled.
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Entering and Storing Data
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
Welco me back. Let's get right to it, shall we? In Pytho n, explicit is better than implicit. The interpreter wo n't try to co nvert the string
"3.14159 " into a number fo r yo u implicitly—if yo u try to add that string to the integer 1, yo u'll get an erro r message:
We'll talk abo ut that mo re later in the lesso n; fo r no w, just let tho se terms marinate in yo ur mind. In the last lesso n, yo u saw ho w
to represent string and numeric data in Pytho n pro grams, and use the print () functio n to send expressio n values to the user.
No w we'll lo o k at ho w to sto re data and ho w to extract data from the user. Because interactive user input arrives to us in string
fo rm, we'll also need to be able to co nvert strings into o ther data types.
In Pytho n, a value is given a name with the assignment statement. In its simplest fo rm, the assignment statement
co nsists o f a name, an equals sign, and a value. The value can be a single data item o r an expressio n.
Click to start a new interactive sessio n, and type these Pytho n statements:
INTERACTIVE SESSION:
cold1:~$ python3
>>> r = 32
>>> pi = 3.14159
>>> area = pi * r ** 2
>>> print(area)
3216.98816
>>> item = { 'link': "http://holdenweb.com", 'value': 99.99 }
>>> targetURL = item['link']
>>> print(targetURL)
http://holdenweb.com
>>> lst = range(5)
>>> print(lst)
range(0, 5)
>>> r = r + 1
>>> print(r)
33
These assignment statements all have pretty much the same fo rmat: nam e = value . They do n't represent
mathematical equatio ns, they are instructions to the computer. The statements are telling the co mputer to asso ciate the
value o n the right side o f the equals sign with the name o n the left side o f it. Once a name is bo und to a value, it stays
that way unless yo u change it.
When yo u read a statement like r = r + 1, be sure to read it like a pro grammer, no t a math student! Fo r us, it means to
take the value currently associated with the name r, add o ne to it, and then asso ciate this new value with the name. So if
the value o f r was 1112 befo re the statement was executed, it wo uld be 1113 afterward.
Names in Python
Every pro gramming language has rules abo ut which names are acceptable. In Pytho n, the interpreter requires
every name to begin with a letter (upper- o r lo wer-case) o r the undersco re character. The rest o f the name can
be made up o f letters, digits, o r undersco res. So , i, _privat e , Cam e lCase , and ve ry_lo ng_nam e _127 are
all valid names. But 12_nam e isn't valid, because it begins with a digit. m y-nam e is also invalid, because it
co ntains a hyphen.
Each Pytho n file yo u create is a module. Each mo dule has its o wn namespace (o ften called the global
namespace). An assignment statement at mo dule level affects the mo dule's glo bal namespace. When the
interpreter needs the value asso ciated with a specific name, it lo o ks fo r the name in a predefined list o f
places. Fo r mo dule-level co de, there are o nly two namespaces to co nsider: the mo dule glo bal namespace
and the built-in namespace that ho lds Pytho n's essential functio ns. Yo u'll learn to write functio ns and classes
later when we create instances o f classes. Every time yo u call a functio n o r create a new class o r instance, the
Pytho n interpreter creates a new namespace. That namespace beco mes unavailable when the related o bject
is destro yed.
Start the interactive interpreter windo w again and try these co mmands (remember, the >>> is a pro mpt, no t
so mething yo u have to type):
INTERACTIVE SESSION:
cold1:~$ python3
>>> a = 23
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'a']
>>> dir(a)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delatt
r__', '__divmod__',
'__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__
ge__', '__getattribute__',
'__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__int__', '__i
nvert__', '__le__',
'__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '_
_or__', '__pos__',
'__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__',
'__repr__', '__rfloordiv__',
'__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rr
shift__', '__rshift__',
'__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__',
'__sub__', '__subclasshook__',
'__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator',
'imag', 'numerator', 'real']
Co nsider these questio ns and answers as they relate to the co de yo u just typed:
cold1:~$ python3
>>> z = 100
>>> a = (3 + z) * 9
>>> print(a)
927
>>> a = \
... (3 + z) * 9
>>> print(a)
927
>>> a = (
... (3 + z)
... * 9
... )
>>> print(a)
927
Let's try a few mo re examples in the interactive interpreter windo w (if yo u clo sed it, click to start a new
interactive sessio n):
INTERACTIVE SESSION:
cold1:~$ python3
>>> a = 1
>>> z = 2
>>> print(a, z)
1 2
>>> a = 1; z = 2
>>> print(a, z)
1 2
>>> a, z = 1, 2
>>> print(a, z)
1 2
In o ur first example, we have a different single assignment statement. Next, tho se same two statements
appear, separated by a semico lo n. Finally, there is an example o f what is called an unpacking assignment.
This has a co mma-separated list o f names o n the left and ano ther list o f values o n the right. Each value is
bo und to the co rrespo nding name.
Indentation
In the pro grams yo u've written so far, all statements have started in the first co lumn o f the line. Statements can
be indented when they are the o bject o f o ne o f Pytho n's compound statements. A set o f statements at the
same indentatio n level (including any co de indented within a statement) fo rm a blo ck, also called a suite. We'll
lo o k mo re clo sely at suites when we discuss co mpo und statements in future lesso ns. Fo r no w, just be sure
to start yo ur lines witho ut any leading spaces.
Comments
In a Pytho n pro gram text, the "#" character (po und sign, o cto tho rp, hash mark, call it what yo u will) intro duces
a co mment. The co mment runs to the end o f the line—it is disregarded by the interpreter. Co mments sho uld
o nly o ccur where whitespace is legal (fo r readability). Co mments help o ther pro grammers to make sense o f
yo ur pro gram, so include them o ften. As yo ur skill level increases, yo ur co mments may be less detailed, but
yo ur co de sho uld always be easy to read fo r bo th intent (the desired result o f the co de) and implementatio n
(the way the co de acco mplishes the intent). Use co mments as necessary to keep yo ur co de readable!
Docstrings
Any Pytho n expressio n is a valid statement (tho ugh statements are never expressio ns). A string o n its o wn,
as the first statement o f vario us Pytho n co nstructs (like mo dule, functio n, class, and metho d), is interpreted by
many to o ls as do cumentatio n. Using a three-quo te string allo ws yo u to add lo ts o f do cumentatio n to yo ur
pro grams. Use do cstrings extensively to do cument yo ur co de. Later examples will sho w yo u so me practical
do cstring co ntent. Fo r no w, let's try a new pro gram. Type this co de in the edito r windo w belo w:
CODE TO TYPE:
#
# This is a program that prints its own docstring
#
"""print_docstring.py prints its own docstring
(the string that's the first executable statement,
which in this case extends to three lines)."""
print(__doc__)
Save the pro gram in yo ur /pyt ho n1 fo lder as print _do cst ring.py, and go to an interactive sessio n to
change to the /pytho n1 fo lder and run it:
INTERACTIVE SESSION:
cold1:~$ cd python1
cold1:~/python1$ python3 ./print_docstring.py
print_docstring.py prints its own docstring
(the string that's the first executable statement,
which in this case extends to three lines).
cold1:~/python1$
__do c__ in t he Mo dule Nam e space : In the co de abo ve, the interpreter reso lves the name
Note __do c__ by lo o king in the mo dule namespace. The name is always present, but if the mo dule
has no do cstring, it is set to the special value No ne .
No w what happens if we remo ve the do cstring—what happens when the print statement runs? Turn the string
into an assignment statement by putting "x = " at the beginning o f the first line after the co mments, as sho wn:
CODE TO TYPE:
#
# This is a program that prints its own docstring
#
x="""print_docstring.py prints its own docstring
(the string that's the first executable statement,
which in this case extends to three lines)."""
print(__doc__)
Save and run it. Can yo u think o f any o ther interesting variatio ns o n this pro gram? Go ahead and try a few o f
yo ur o wn experiments!
Do not use any literal strings—write expressions using methods of s only! Fo r example: s.capit alize ().
To see a list o f the metho ds o f a string, use dir(" " ) in the interactive interpreter.
Save it in yo ur /pyt ho n1 fo lder as case _co nve rt .py and run it.
Test yo ur pro gram o n vario us strings by mo difying the assignment statement. Play with using the dir("string")
functio n to investigate string metho ds. Fo r example, try s.capitalize(), s.islo wer(), s.swapcase()...
It's a little tedio us to have to edit the pro gram each time yo u want to see what happens with a new value,
right? Next we will lo o k at a way o f allo wing the user to pro vide the strings that o ur pro gram o perates o n and
avo id all that extra wo rk!
A co uple o f lines o f input are sho wn in the fo llo wing screen sho t. No tice that the input () functio n always
returns a string—even when the user actually types in a number:
T ype Conversions
As we mentio ned earlier, in Pytho n, "explicit is better than implicit," so we canno t add a string (even a string
who se value is a valid number) to a number. Instead, we have to explicitly co nvert the string first. The int ()
functio n takes a single string as an argument, and returns the integer represented by the string (o r raises an
exceptio n). The f lo at () functio n is similar, but takes any valid string representatio n o f a flo ating-po int number
instead (again raising an exceptio n if the string canno t be co nverted).
Yo u'll need an interactive terminal no w. Remember, if yo u do n't have o ne available, just click the ico n.
Type in these co mmands interactively:
INTERACTIVE SESSION:
cold1:~$ python3
>>> n = int(input("Enter a number: "))
Enter a number: 33
>>> x = float(input("Another number: "))
Another number: 45.67
>>> n, x
(33, 45.67)
>>> y = float(input("Final number: "))
Final number: abc.def
Traceback (most recent call last):
File "<console>", line 1, in <module>
ValueError: could not convert string to float: abc.def>>>
Feel free to try o ther inputs. Observe, to o , that the flo ating-po int number system used o n co mputers canno t
express 45.6 7 exactly, tho ugh it gets pretty clo se. This usually o nly happens with flo ating-po int numbers, no t
integers. If yo u haven't pro grammed befo re, just remember these "ro unding erro rs" make arithmetic slightly
inexact, so be sure they do n't make a difference to yo ur results. They can so metimes add up surprisingly
quickly. In the last o f the three cases abo ve, the user is entering text that canno t be co nverted into a number.
So Pytho n calls the actio n to a halt with an exception traceback that tells yo u what happened. (Pretty co o l,
huh?)
Because the o bservatio ns were made in an interactive interpreter after the traceback, yo u see ano ther >>>
pro mpt. If an unhandled exceptio n o ccurs when running a pro gram, the pro gram run is terminated. But this
isn't always yo ur desired result. Fo rtunately, there are ways yo u can handle these exceptio ns and avo id
pro gram terminatio n. Fo r no w, let's just type carefully when we need to pro vide numeric input!
CODE TO TYPE:
#
# wall_area.py
#
h = float(input("Room height: "))
w = float(input("Room width : "))
d = float(input("Room depth : "))
area = 2 * (h * (w + d))
print("Area of walls:", area)
Save it in yo ur /pyt ho n1 fo lder as wall_are a.py, and run it in an interactive sessio n a few times, using different
inputs:
INTERACTIVE SESSION:
What happens if yo u give the pro gram a no n-numeric input? (Never fear. We'll sho w yo u ho w to deal with tho se
circumstances later.)
Getting It Done
We're co vering a lo t o f material in these early lesso ns, and we still have a lo ng way to go . Yo u're do ing really well so
far—stick with it—see yo u in the next lesso n!
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Making Decisions: The if Statement
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
So far, we've co vered basic types o f statements in Pytho n—the assignment statement and the statement that calls a functio n.
The functio n we've used mo st o ften is print (), which has the handy side effect o f printing o ut the value o f o ne o r mo re
expressio ns.
One o f the mo st co mmo n needs in pro gramming is to make a decisio n and act o n it—fo r example, do ing o ne thing if a
custo mer answers Yes to a questio n, and ano ther if they answer No . The if statement gives us this po wer, allo wing us to take
different actio ns depending o n whether a specific condition is true.
Conditions in Python
In o rder to be able to make a decisio n, yo u need to evaluate so me co nditio n. The co nditio ns we co mpare mo st
frequently in Pytho n are o f two values, using the co mpariso n o perato r. Yo u can co mpare fo r vario us kinds o f equality
o r inequality:
Co mparing numbers is pretty intuitive, but keep in mind that yo u can't use these o perato rs to co mpare co mplex
numbers. The o perands are two -dimensio nal, so they can't be laid o ut in a straight line; simple co mpariso ns like that
aren't valid. Instead, yo u must co mpare the abso lute values o f co mplex numbers, using the abs() functio n.
Co mparing strings is fairly straightfo rward as lo ng as yo u can alphabetize a list o f items. The characters in strings
have a defined o rder, so metimes called the collation sequence. Let's suppo se we want to co mpare strings a and b.
The interpreter lo o ks at the first character o f each string. If the first character o f a o ccurs earlier in the co llatio n sequence
than the first character o f b, then a is less than b. If the first character in a is greater than the first character in b, then a is
greater than b. If this initial attempt to co mpare the strings is inco nclusive, then the next characters in the sequence o f
the strings are co mpared until a determinatio n is made.
If the end o f o ne o f the strings is reached and additio nal characters still remain in the o ther, then the lo nger o f the two
strings is greater. If bo th strings have exactly the same characters in them, they are co nsidered equal. Yo u may see the
term "lexical co mpariso n" used to describe this metho d o f co mparing strings. Start a new terminal sessio n and verify
the fo llo wing results:
INTERACTIVE SESSION:
cold1:~$ python3
>>> a = 23.0
>>> b = 22
>>> a == b
False
>>> a != b
True
>>> a < b
False
>>> a <= b
False
>>> a > b
True
>>> a >= b
True
>>> p1 = "Python"
>>> p2 = "Perl"
>>> p1 == p2
False
>>> p1 != p2
True
>>> p1 < p2
False
>>> p1 <= p2
False
>>> p1 > p2
True
>>> p1 >= p2
True
>>> "this+" > "this"
True
>>> "that" == "that"
True
>>> "That" == "that"
False
>>> "That".upper() == "thAT".upper()
True
>>>
The last tests sho w that string co mpariso ns are case-sensitive. If yo u want to avo id case-sensitivity, use the uppe r()
o r lo we r() metho d to co nvert bo th strings to the same case.
In additio n, yo u can determine whether o ne string appears inside ano ther, using the in test:
INTERACTIVE SESSION:
cold1:~$ python3
>>> x = "nan"
>>> s = "Banana"
>>> x in s
True
The result o f the expressio n x in s is True when the substring x appears so mewhere inside the string s. Yo u can also
test to find o ut whether a string is a member o f a list o r a tuple (a tuple is a sequence o r o rdered list, o f finite length); x
in lt is true if x is an element o f lt , whether lt is a list o r a tuple.
Also , strings have several metho ds fo r yo u to use to determine whether the string has specific characteristics. The
mo st impo rtant o nes are sho wn in this table:
All o f these co nditio ns can be tested individually o r, as we'll see later, in co mbinatio n. Yo u can use the if statement to
cho o se whether o r no t to execute o ne o r mo re statements by testing a co nditio n and executing the statement if the
co nditio n is true. Yo u can also cho o se which sets o f statements to execute.
The if statement begins with the keywo rd if , fo llo wed by an expressio n, and ends with a co lo n. This line is always
fo llo wed by an indented suite—o ne o r mo re statements with an indentatio n level greater than that o f the if line. If the
co nditio n is true, then the indented suite is executed. All the statements in the suite must be indented to exactly the
same level. In the Pytho n wo rld, we use fo ur additio nal spaces fo r each new indentatio n level.
CODE TO TYPE:
#!/usr/local/bin/python3
"""Detect any mention of Python in the user's input."""
By default, the Co deRunner edito r and yo ur web bro wser likely display tab characters as taking up eight
spaces. It is pro per Pytho n pro gramming practice to indent fo ur spaces. As such, we reco mmend that
Note yo u indent co de using the space bar rather than the Tab key. Alternatively, if yo u are familiar with a Linux
text edito r, yo u are welco me to use emacs o r vim, which are available o n the OST server.
INTERACTIVE SESSION:
cold1:~$ cd python1
cold1:~/python1$ ./find_python.py
Please enter a sentence: My python is a happy snake.
You mentioned Python.
cold1:~/python1$ ./find_python.py
Please enter a sentence: There's a pylon in the road.
cold1:~/python1$
Run the pro gram several times to verify that when the string "pytho n" is present in yo ur input, the pro gram prints "Yo u
mentio ned Pytho n." and when "pytho n" is NOT present, it do es no t. Make sure yo u test in all circumstances.
Because we added # !/usr/lo cal/bin/pyt ho n3 at the beginning o f the pro gram, we didn't need to call
Note pyt ho n3 specifically in the interactive sessio n. We'll use this fro m no w o n in o ur pro grams, but it might
no t wo rk o n o ther systems.
Choosing Between Alternatives: the else Clause
The basic if statement allo ws yo u to cho o se whether to execute an indented suite made up o f o ne o r mo re
statements. If yo u want to execute o ne set o f actio ns if the co nditio n is true and execute ano ther set if it is false, add an
e lse clause to the if statement. The e lse clause fo llo ws the first indented suite, and is fo llo wed by the indented suite
to execute if the if co nditio n is false. When the co nditio n is true, the first suite is executed; when it is false, the seco nd
suite is executed. Mo dify f ind_pyt ho n.py as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Detect any mention of Python in the user's input."""
Save and run it. Test yo ur pro gram several times, using bo th types o f input. When yo ur pro gram includes alternative
behavio rs, it's impo rtant to test all the po ssible paths.
OBSERVE:
if (condition 1):
suite 1
else:
if (condition 2):
suite 2
else:
if (condition 3):
suite 3
else:
...
To o verco me this, Pytho n has the e lif keywo rd, which yo u can use instead o f e lse ... if . Because a single e lif
inco rpo rates the functio ns o f bo th the e lse and the if statements, e lif do es no t intro duce an additio nal level o f
indentatio n:
OBSERVE:
if (condition 1):
suite 1
elif (condition 2):
suite 2
elif (condition 3):
suite 3
else:
...
Bo th o f o ur examples do the same thing, but the seco nd o ne is easier to read, and presents the chain o f cho ices much
mo re clearly. The e lse clause at the end is o ptio nal; if it's included, then the suite under it will be executed if no ne o f
the co nditio ns are true. Witho ut an e lse clause, the pro gram wo n't do anything at all if no ne o f the co nditio ns are true
(it will just co ntinue o n the line after suite 3).
No w suppo se we want to analyze a user's input to detect different pro gramming languages, and respo nd if we do n't
find any o f o ur languages mentio ned. Mo dify yo ur pro gram so that it uses e lif to select amo ng the alternatives. Edit
f ind_pyt ho n.py again so it lo o ks like this:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Detect any mention of several languages in the user's input."""
Save and run the pro gram. Test yo ur results a few times. The first three times, mentio n o ne o f the target languages;
the fo urth time do n't mentio n a language at all. No w let's po nder a few questio ns to gether: What happens if o ur input
co ntains two languages? Do es the pro gram detect them bo th? Why o r why no t?
INTERACTIVE SESSION:
cold1:~$ python3
>>> s = "ABC"
>>> if s.isupper() and s.startswith("A"):
... print("s is upper case starting with A")
...
s is upper case starting with A
>>>> s = "BBC"
>>> if s.isupper() and s.startswith("A"):
... print("s is upper case starting with A")
...
(Nothing prints.)
>>> if 1 == 2 or s.endswith("C"):
... print("Impossible happened or s ends with C")
...
Impossible happened or s ends with C
>>>
If two co nditio ns are jo ined by and, the result is true o nly if both co nditio ns are true. If two co nditio ns are jo ined by o r,
the result is true if either co nditio n is true, so even tho ugh 1 can never be equal to 2, in the seco nd example, the
co nditio n was still true.
Here are so me o f the o ther tests yo u can create with if statements. This pro gram uses the while statement. Create
this new pro gram as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
target = 63
guess = 0
Save it in yo ur /pyt ho n1 fo lder as gue sse r.py and run it, entering a few guesses. Fo r every guess yo u make, the
pro gram repo rts whether yo ur guess is to o high o r to o lo w. With every guess, yo u clo se in o n the target number.
Belo w is the o utput fo r a typical run o f the pro gram:
INTERACTIVE SESSION:
cold1:~/python1$ ./guesser.py
Guess an integer: 22
Too low...
Guess an integer: 88
Too high...
Guess an integer: 50
Too low...
Guess an integer: 67
Too high...
Guess an integer: 58
Too low...
Guess an integer: 63
Just right!
Wrapping It Up
Yo u're lo o king go o d so far. But there's plenty mo re to learn still!
In the next lesso n, we'll lo o k at ho w we can write mo re po werful pro grams using loops. See yo u there!
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Iteration: For and While Loops
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
use a f o r statement to lo o p o ver the characters in a string yo u enter, and an if statement to determine whether each
character is a vo wel.
execute a bre ak during a lo o p to terminate the lo o p immediately.
Glad to see yo u here again! We've co vered a co uple o f basic "o bject" types in Pytho n—strings and numbers. We've also
enco untered o ne o f Pytho n's sequence types—strings. Other Pytho n sequence types may co ntain mo re than just characters.
But befo re we try to understand Pytho n's co ntainer o bjects, let's take a lo o k at lo o ps.
Mo dern co mputers execute millio ns o r even billio ns o f instructio ns per seco nd. If we had to write o ut every instructio n, it wo uld
take a lifetime to write less than a seco nd's wo rth o f co de. Fo rtunately, we have lo o ps at o ur dispo sal. Lo o ps tell the co mputer
to execute the same sequence o f actio ns o ver and o ver. Thro ugh the use o f lo o ps, we can tell the co mputer to repeat the same
o peratio ns o n different pieces o f data, witho ut typing the instructio ns again each time.
At the end o f the last lesso n we wo rked with a pro gram that co ntained a while lo o p. Using a while lo o p, we can apply the same
lo gic repeatedly—in this case, until a specific co nditio n is true. Similarly, f o r lo o ps allo w yo u to repeat the same actio ns o n
each o f a number o f things. We'll take a clo ser lo o k at f o r lo o ps first.
Let's try an example that uses a f o r statement to lo o p o ver the characters in a string yo u enter, and an if statement to
determine whether each character is a vo wel. Type this co de in the edito r windo w:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Counts the vowels in a user input string."""
Save it in yo ur /pyt ho n1 fo lder as vo we l_co unt e r.py and run it in the Terminal tab. The "Enter any string:" pro mpt
appears. The pro gram co unts the number o f vo wels in any string yo u enter and returns a to tal.
The f o r statement is fo llo wed by an indented suite (in this case, a single if statement). When the f o r statement
executes, the name s is bo und to a string. Fo r each character in the string, the interpreter executes the indented suite
with the name c, bo und to the current character. So , suppo se yo u entered "the." The first time thro ugh the lo o p, c wo uld
be "t." The seco nd time, it wo uld be "h," and the third time, it wo uld be "e." Each time aro und, the if statement checks to
see whether c co ntains a vo wel. If it do es, we add 1 to the vco unt co unter; o therwise no thing happens because there
is no e lse clause fo r the if . After all characters have been pro cessed, vco unt co ntains a co unt o f the vo wels within
the input string.
We had the pro gram print every time it went thro ugh the lo o p so yo u co uld see ho w it wo rks. No w, let's change o ur
pro gram to print o nly when it finishes reading the input string. To do that, we simply unindent the print statement so that
it falls o utside o f the lo o p. We'll also remo ve the co de that prints the value o f "c":
CODE TO TYPE:
#!/usr/local/bin/python3
"""Counts the vowels in a user input string."""
Suppo se yo u wanted to kno w where the first space appears in a string. One way to find o ut wo uld be to lo o p thro ugh
the string, co unting characters until yo u fo und a space. But o nce yo u fo und it, ho w wo uld yo u sto p co unting? Yo u
co uld set a flag to tell yo ur co mputer to sto p co unting after yo u fo und the space and co mpleted the lo o p, but then why
bo ther co mpleting the lo o p? It wo uld be mo re efficient to sto p lo o king at characters o nce yo u fo und the first space. To
do that, we use the bre ak statement. If yo u execute a bre ak during a lo o p, the lo o p terminates immediately.
Let's write a pro gram that prints o ut the po sitio n where the first space appears in a string. Enter this co de:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Program to locate the first space in the input string."""
INTERACTIVE SESSION:
cold1:~$ cd python1
cold1:~/python1$ ./space_finder.py
Please enter a string: Space, the final frontier.
First space occurred at position 6
The co unt (po s) starts at 0 because that's the first po sitio n o f a string o r any o ther Pytho n sequence. Each time
thro ugh the lo o p, we test to see if the current character (c) is a space (" "). If it is, we hit the bre ak, exit the lo o p, and
print the value fro m po s; o therwise we add 1 to po s and the lo o p co ntinues. Be sure yo u get things in the right o rder!
Incrementing the co unt befo re testing and terminating the lo o p wo uld cause what's kno wn as an "o ff by 1 erro r."
But what do es the pro gram do if there's no space in the input? I'm glad yo u asked! It prints o ut a result as tho ugh a
space fo llo wed the input string, because the lo o p terminates after it has inspected each character in the string. Check it
o ut in yo ur pro gram by running it with an input co ntaining no spaces.
We need separate lo gic to verify that there really is a space in the string. Pytho n lo o ps co me with such extra lo gic built
in, in the shape o f the o ptio nal e lse clause. This clause is placed at the same indentatio n level as the f o r o r while
lo o p that it matches, and (just as with the if statement) is fo llo wed by an indented suite o f o ne o r mo re statements.
This suite is only executed if the lo o p terminates no rmally. If the lo o p ends because a bre ak statement is executed,
then the interpreter just skips o ver the e lse suite. In the case o f the space detectio n pro gram, we execute the bre ak
when we detect a space, so an e lse clause o n the f o r lo o p wo uld o nly run if there were no spaces in the input.
We need to mo dify o ur co de a bit mo re. In the first versio n, the print was lo cated at the end o f the lo o p, where it
always runs no matter what the o utco me o f the testing. No w we want it to be part o f the suite guarded by the if
statement, so it o nly runs when a space is fo und. Mo dify yo ur space_finder.py file as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Program to locate the first space in the input string."""
Save and run it with a string that co ntains a space and then with a string that do esn't. The pro gram runs just fine either
way.
As yo ur pro grams beco me mo re co mplex, yo u will find that there are so metimes different ways to express the same
lo gic. In tho se cases, yo u sho uld "do the simplest thing that wo rks." Fo r example, in the bo dy o f the lo o p, we could
have put the statement that increments the co unter in the suite o f an e lse clause. We cho se no t to use an e lse
because if the expressio n c == " " tests as true, the bre ak statement will guarantee that po s isn't incremented (by
immediately exiting fro m the lo o p) befo re the assignment statement is executed.
While Loops
The f o r lo o p is useful when yo u want to apply the same lo gic to each member o f a sequence. But so metimes (like in
the guessing game at the end o f the last lesso n) yo u do n't have a finite sequence; yo u want actio ns to be repeated
until so me co nditio n is true.
Suppo se we want to split a string into wo rds. Defining wo rds by the spaces between them, we can lo cate the first
space with a f o r lo o p. No w we can mo dify the string each time we find a wo rd (by re-binding the name o f the string to a
new string with the wo rd remo ved) until there are no mo re wo rds left. That's the idea behind the next pro gram. Create a
new file as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Program to split a sentence into words."""
Save it in yo ur /pyt ho n1 fo lder as se nt e nce _split t e r.py and run it. The while T rue clause in this pro gram sets up
a lo o p that will keep running until lo gic in the if /e lse suites causes a break. When yo u see while T rue in a pro gram,
either the pro grammer has included a bre ak statement to terminate the lo o p, o r the pro gram is designed fo r
co ntinuo us o peratio n—or the pro grammer has made a terrible mistake and the pro gram will never sto p running! In this
case, it's the fo rmer: the bre ak to terminate the while lo o p is inside the f o r lo o p's e lse clause. Run the pro gram and
enter a sentence. Yo u sho uld see each wo rd in the sentence o n a separate line.
Note In s[:po s] and s = s[po s+1:], we use a feature that we haven't co vered yet. Stay tuned!
Of co urse this pro gram isn't perfect—very few pro grams are! Try entering a sentence where the wo rds are separated by
multiple spaces. The pro gram prints empty lines, co rrespo nding to the "empty wo rds" between the spaces. We can fix
that, tho ugh. One way wo uld be to remo ve leading spaces befo re go ing into the f o r lo o p each time. Let's mo dify o ur
sentence_splitter.py to allo w multiple spaces between wo rds. Edit the pro gram as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Program to split a sentence into words."""
When yo u run yo ur updated pro gram, yo u can enter as many spaces as yo u like between the wo rds and still get o ne
wo rd per line in the o utput. Can yo u figure o ut ho w yo u might use o r to igno re extra tabs between wo rds? What part o f
the pro gram wo uld yo u need to change to treat tabs as co mpletely equivalent to spaces? (Hint: yo u wo uld have to
accept sentences with just tabs between the wo rds.)
In the final example fo r this lesso n, we'll pro cess lines o f input fro m the user. The user will indicate the end o f their
input by entering a blank line (simply pressing the Ent e r key), but we want them to be able to add co mments to their
input by entering lines beginning with the # character. These lines sho uldn't be pro cessed; they are just there to info rm
the reader. (Pytho n also accepts co mments—the # character tells the interpreter to igno re everything else up to the end
o f the line). We aren't especially co ncerned with the pro cessing that's do ne o n each line, so in this example we'll just
use the le n() functio n to print.
A co mment sho uld be indicated by the first printable character. Create a new file as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Demonstrating the continue statement."""
while True:
s = input("Enter a line (or Enter to quit): ")
if not s:
break
if s.startswith("#"):
continue
print("Length", len(s))
Save it in yo ur /pyt ho n1 fo lder as le ngt h_co unt e r.py and run it. Enter several lines, including at least o ne
co mment line that begins with "#." Co mment lines are pro cessed differently fro m regular lines because o f the
co nt inue statement, which immediately causes the pro gram to lo o p and ask fo r ano ther input. There are o ther ways
yo u co uld have achieved the same result.
We must co nfess, there are easier ways to perfo rm the tasks that yo u pro grammed in this lesso n, but we want yo u to
understand what's go ing o n behind the scenes first. Start o r switch to an interactive sessio n and enter the fo llo wing
expressio ns:
INTERACTIVE SESSION:
The f ind() string metho d lo cates a given character inside the string (it finds the first o ccurrence o f the string passed as
its argument). The split () string metho d, when called witho ut arguments, splits the string up into its co nstituent wo rds,
which are assumed to be separated by o ne o r mo re white space characters. The strings inside the square brackets
co nstitute a list. We'll be lo o king at tho se (and their co usins, the tuples) in the next lesso n.
Well do ne. Go o d fo r yo u fo r sticking with it! No w yo u have a grasp o f a lo t o f the basics, yo u'll be able to take o n mo re
co mplex pro gramming challenges. See yo u at the next lesso n!
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Sequence Containers: Lists and Tuples
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
Welco me back, my favo rite student! So far, we've co vered basic o bject types in Pytho n, such as strings and numbers. No w
we're ready to lo o k at Pytho n's "co ntainer" o bjects, starting with lists and tuples. Bo th lists and tuples are sequence types.
Because strings are sequence types as well, much o f what we learn here can be applied to strings as well.
Sequence types have a specific order, so it's easy to spo t a string's first and last characters. Similarly, lists and tuples present
elements in a particular o rder. Each element o f a sequence is numbered. The numbering always starts at zero . Yo u refer to an
individual element by fo llo wing the sequence name with a number in square brackets [ ].
Tuples are usually written as a co mma-separated list o f values surro unded by parentheses ( ) rather than brackets [ ],
but in many cases the parentheses () are o ptio nal. (Okay, I will refrain fro m including illustratio ns o f brackets [ ] and
parentheses ( ) no w. Yo u get the picture, right?) The interactive interpreter always puts parentheses aro und a tuple
when asked to display its representatio n, and we reco mmend yo u do the same, in o rder to facilitate readability.
cold1:~$ python3
>>> lst1 = [1, 3, 5]
>>> lst2 = [2, 4, 6]
>>> tup1 = (9, 7, 5)
>>> tup2 = (8, 6, 4)
>>> dir(lst1)
['__add__', '__class__', '__contains__', ... , 'append', 'count', 'extend',
'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> dir(tup1)
['__add__', '__class__', '__contains__', ... , 'count', 'index']
>>> lst1+lst2
[1, 3, 5, 2, 4, 6]
>>> tup1+tup2
(9, 7, 5, 8, 6, 4)
>>> lst1+tup1
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: can only concatenate list (not "tuple") to list
>>> clist = [lst1, lst2, tup1, tup2]
>>> print(clist)
[[1, 3, 5], [2, 4, 6], (9, 7, 5), (8, 6, 4)]
The results o f the calls to the dir() functio n sho w that a list has metho ds that a tuple do esn't have. We abbreviated the
o utput by remo ving many o f the special names beginning with a do uble undersco re. We'll learn mo re abo ut tho se
metho ds later, but fo r no w we'll fo cus o n the regular metho ds.
Yo u can put whatever yo u like in a list. Usually yo u'll enter simple values like strings and numbers. The clist list in the
example abo ve co ntains two o ther lists and two tuples.
INTERACTIVE SESSION:
Indexing and slicing are fundamental o peratio ns in Pytho n, so make sure that yo u understand why each expressio n
evaluates the way it do es. Be aware that when yo u slice a sequence, the seco nd index isn't the index o f the last
element in the slice. This is actually very useful. It wo uld be co nfusing if clist [2:4 ] didn't give yo u a list 2 elements
lo ng, so element 4 isn't included in that slice. So , to get the fo urth element in o ur slice, we referenced the no nexistent
element 5. Because strings are also sequences, we can cho p strings up witho ut to o much difficulty.
Modifying Lists
Altho ugh strings and tuples are also sequences, they are immutable. Once created, they can't be changed (altho ugh
yo u can still index and slice them to extract individual elements o r sub-sequences). Lists, ho wever, can be changed. In
the same way that yo u can bind a new value to a name with an assignment, yo u can also bind a new value to an
element o f a list. Let's check o ut o ne way yo u can mo dify a list. Start o r co ntinue the interactive terminal sessio n as
sho wn:
INTERACTIVE SESSION:
So far, we've just been just replacing single elements o f the list. It's also po ssible to replace a slice. When yo u do that,
make sure that yo u also assign ano ther sequence. Any sequence will do —a list, tuple, o r string. If yo u assign a string
to a slice, each character in the string beco mes a new element o f the list. Try experimenting with these po ssibilities.
Because yo u can replace any slice o f a list, yo u can delete the slice by assigning an empty sequence to it. But there are
less labo r-intensive ways to replace a slice o f a list. Pytho n's de l statement was designed especially fo r deleting
things. Yo u can use it o n a single element o r a slice. If yo u kno w that a list co ntains a certain value, but yo u do n't kno w
the value's index, yo u can use the list's re m o ve () metho d to delete it fro m the list. If the same value o ccurs mo re than
o nce, o nly the first o ccurrence is deleted. Let's give it a try. Type the co mmands belo w as sho wn:
INTERACTIVE SESSION:
As we saw in an earlier example, we can add elements to a list. Ano ther way to include mo re elements is to use the
list's appe nd() metho d. Yo u call the metho d and give it a new element to be appended to the list. It's also po ssible to
insert elements at a specific po sitio n, and again there are two ways to do this. The simplest way is to use the list's
inse rt () metho d, which yo u call with a po sitio n (index value) and a value to be inserted. Or yo u co uld also assign the
new value to an empty slice—any slice with the same value fo r the lo wer and upper indexes is bo und to be empty.
Let's experiment with adding new elements to a list. Type the co mmands belo w as sho wn:
INTERACTIVE SESSION:
One o f the limitatio ns we run into with slice assignment is that the replacement must be a sequence, so we usually
append o r insert it. If yo u have a sequence o f elements that yo u want to insert, keep in mind that slice assignment
requires much less co de than mo st o ther techniques.
If yo u call a list's appe nd() metho d with a sequence argument (like yo u did with e list .appe nd((1, 2, 3))
Note in the example abo ve), that entire sequence beco mes the last element o f the list.
When yo u specify o nly two slice co mpo nents, by default the stride is 1; it takes every element in the slice. A stride o f 2
takes every seco nd element, and so o n. Stride values can be negative as well as po sitive. Slicing always wo rks by
setting the index to the first slicing co mpo nent and then increasing the index by the stride value, until the index reaches
o r go es past the seco nd slicing co mpo nent. When the stride is negative, the first slicing co mpo nent must be higher
than the seco nd. Type the co mmands belo w as sho wn:
INTERACTIVE SESSION:
One way to get the reverse o f a sequence is to slice the who le thing by o mitting the first and seco nd slice co mpo nents
and then use a stride o f -1. So , if yo u want to replace a list with its reverse, rather than use the list's reverse() metho d
lst.reverse(), yo u can write lst = lst[::-1]. Pytho n sequences are no thing if no t versatile!
If yo u give a seco nd argument, it sho uld be an integer. This info rms the interpreter o f the maximum number o f times to
reco gnize the separato r, which limits the number o f elements in the returned list.
To get the sum o f the numbers in a sequence, pass the sequence to the sum () functio n as an argument. The
interpreter will raise an exceptio n if there is a no n-numeric element in the sequence. To get the length o f any sequence,
use the le n() functio n.
To find the number o f times a particular element appears in a list o r tuple, use the co unt () metho d, with the element
value yo u are seeking as the argument.
CODE TO TYPE:
#!/usr/local/bin/python3
"""Simpler program to list the words of a string."""
Save it in yo ur /pyt ho n1 fo lder as be t t e r_se nt e nce _split t e r.py and run it. This co de perfo rms the same tasks as
the o ne we wro te in the last lesso n, but it uses features built into the Pytho n language. No w type in a string that
co ntains so me white space, press Ent e r, and examine the result. Yo u sho uld see the list o f wo rds, printed o ne per
line.
OBSERVE:
This co de applies the st rip() metho d to string s, which returns a string with no leading o r trailing white space. The
split () metho d is then applied to the already stripped string, returning a list o f the wo rds. The f o r lo o p iterates o ver the
list, printing each wo rd o n a separate line.
No w let's do so mething a little mo re co mplex with lists. We'll take a lo ng piece o f text and find o ut ho w many lines,
wo rds, and characters it co ntains. To determine the number o f characters, use the le n() metho d. We co unt the lines by
splitting the text to get a list o f them. Finally, we split each line into wo rds and accumulate a to tal by adding the number
o f wo rds in each line to gether.
CODE TO TYPE:
#!/usr/local/bin/python3
"""Count the words, lines and characters in a chunk of text."""
gettysburg = """\
Four score and seven years ago our
fathers brought forth on this continent,
a new nation, conceived in Liberty, and
dedicated to the proposition that
all men are created equal.
charct = len(gettysburg)
lines = gettysburg.split("\n")
linect = len(lines)
wordct = 0
for line in lines:
words = line.split()
wordct += len(words)
print("The text contains", linect, "lines,", wordct, "words, and", charct, "characters.
")
Save it in yo ur /pyt ho n1 fo lder as paragraph_st at s.py and run it. If yo u typed in exactly the same input text, yo u'll
see T he t e xt co nt ains 16 line s, 10 2 wo rds, and 5 5 7 charact e rs.
So me o perating systems may give different results; fo r example, Unix reco rds a newline as o ne
Note character, while Windo ws reco rds it as two .
Okay, no w let's mo dify o ur pro gram to keep a co unt o f wo rd lengths, so we kno w ho w many o ne-letter, two -letter, and
three-letter wo rds there are, and so o n. Mo dify yo ur paragraph_stats.py file as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Count the words, lines and characters in a chunk of text."""
gettysburg = """\
Four score and seven years ago our
fathers brought forth on this continent,
a new nation, conceived in Liberty, and
dedicated to the proposition that
all men are created equal.
lines = gettysburg.split("\n")
linect = len(lines)
wordct = 0
for line in lines:
words = line.split()
wordct += len(words)
for word in words:
lengthct[len(word)] += 1
print("The text contains", linect, "lines,", wordct, "words, and", charct, "characters.
")
for i, ct in enumerate(lengthct):
if ct:
print("Length", i, ":", ct)
In the new pro gram, we begin by creating a list o f co unts. The idea is that the co unt o f n-letter wo rds will be kept in
le ngt hct [n], and we assume that no wo rd will be lo nger than 19 characters. So metimes that kind o f assumptio n can
be dangero us, but fo r no w, fo r experimentatio n's sake, we'll just go with it. In the lo o p that pro cesses each line, we
have added a lo o p to iterate o ver the wo rds. The length o f each wo rd is used as an index into the le ngt hct list, and
that element is incremented by o ne (they all start at zero ). Finally, when the text has been fully pro cessed, there is a bit
mo re co de used to o utput the co unt o f wo rds o f each length. The o nly real wrinkle here is the if statement that o mits
tho se lengths fo r which there aren't any wo rds.
It Slices, It Dices...
Yo u've learned quite a bit abo ut Pytho n's sequence types and just ho w useful they can be. Next, we'll check o ut
Pytho n's mapping types.
Phew. This isn't easy, but yo u're do ing really well. Keep it up, and I'll see yo u in the next lesso n!
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Sets and Dicts
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
create sets.
use sets and dicts.
Go o d to have yo u back! We'll go o ver so me backgro und info rmatio n first and then get to wo rk o n so me examples. Lists and
tuples are versatile data structures, but they have o ne fundamental pro perty that yo u just can't get aro und: elements are retrieved
by number. That's fine when yo u're wo rking with elements that are numbered, but o n o ccasio n yo u'll want to be less specific.
That's when sets and dicts co me in handy.
Sets are similar to lists. Yo u can use the in keywo rd to find o ut whether a particular value appears as an element within a list o r
a set. The interpreter finds members within lists by checking each element o f the list, o ne after the o ther, until it finds the value it's
lo o king fo r, o r gets to the end o f the list. The interpreter finds elements in sets using a much faster metho d "under the ho o d"
than the linear scan used fo r lists.
Using a list is fine when yo ur pro gram co ntains just a few elements, but the number may gro w o ver time, particularly when the
data is being sto red in a file o r a database. As the number o f elements in yo ur pro gram gro ws, pro gram perfo rmance beco mes
increasingly slo w. That co uld cause pro blems. In tho se cases, it's better to use a set in the first place.
The same value can appear multiple times in a list, but in a set, a value can appear o nly o nce. When yo u "add" an element to a
set that co ntains that particular element already, the set remains the same. Because o f this feature, yo u can't predict the o rder in
which the set elements will o ccur if yo u lo o p o ver them with a f o r lo o p. When yo u add an element, the o rder may change
co mpletely. In o ther wo rds, altho ugh sets are collections o r containers, sets aren't sequences. There is no co ncept o f "po sitio n"
fo r set elements. Co nversely, in a list, yo u can determine the po sitio n o f a given element, using the inde x() metho d.
Dicts are similar to lists as well. A dict sto res values that can be retrieved by indexing, but the index values in a dict do n't need to
be numerical. All o f this will make mo re sense after we wo rk thro ugh a few examples.
Creating Sets
Yo u write a set as a co mma-separated list o f elements inside braces { }—fo r example, yo u'd type the first three natural
numbers as {1, 2, 3} . Yo u can also use Pytho n's built-in se t () functio n. This is usually called with a single sequence
argument, and creates a set that co ntains all the sequence's elements.
Pytho n includes two separate data types fo r handling sets. As with lists and tuples, yo u build regular sets, then yo u can
add o r remo ve elements. Yo u can also build fro zen sets, which stay the same o nce yo u have created them and raise
an exceptio n at any attempt to change them. A set is an uno rdered co llectio n o f items with no duplicate elements; lists
are o rdered and sets are no t. Set o bjects also suppo rt vario us o peratio ns like unio n, intersectio n, and difference. If
yo u're no t familiar with all this stuff yet, do n't panic! We'll go o ver all o f it in detail here and in later lesso ns.
cold1:~$ python3
>>> {1, 2, 3, 1, 2, 3, 1, 2, 3, 1}
{1, 2, 3}
>>> vowels1 = {"a", "e", "i", "o", "u"}
>>> vowels2 = set("aieou")
>>> vowels1 == vowels2
True
>>> languages = {"perl", "python", "c++", "ruby"}
>>> languages.add("php")
>>> languages
{'python', 'php', 'ruby', 'c++', 'perl'}
>>> "perl" in languages
True
>>> "java" in languages
False
>>> {'python', 'ruby'} < languages
True
>>> set("the quick brown fox") & vowels1
{'i', 'u', 'e', 'o'}
>>> vowels1 - set("the quick brown fox")
{'a'}
>>> set("the quick brown fox") - vowels1
{' ', 'c', 'b', 'f', 'h', 'k', 'n', 'q', 'r', 't', 'w', 'x'}
>>>
Note In the result fro m se t (" t he quick bro wn f o x" ) & vo we ls, the duplicate elements are eliminated.
The examples abo ve used integers, characters, and strings, but mo st Pytho n o bjects can be elements o f a set. Yo u
can co mpute the intersection o f two sets using the & o perato r, and the difference between two sets with the - o perato r.
There are a number o f o ther o peratio ns yo u can perfo rm o n sets as well. Many, but no t all, o f the o peratio ns can be
perfo rmed using either o perato rs o r a metho d call o n o ne o f the sets.
CODE TO TYPE:
#!/usr/local/bin/python3
"""Count the number of different words in a text."""
text = """\
Baa, baa, black sheep,
Have you any wool?
Yes sir, yes sir,
Three bags full;
One for the master,
And one for the dame,
And one for the little boy
Who lives down the lane."""
This is a classic pro blem we can run into when wo rking with text in o ur pro grams. Pytho n lets yo u so lve it in a unique
way. First, it uses a f o r lo o p to remo ve all the punctuatio n (punc) characters (,?;.) fro m the string, replacing each o ne
with an empty string (""). Next, it prints the text so yo u can co nfirm that the punctuatio n has been remo ved. Finally, it
co nverts the text to lo wer-case, splits the text at each run o f white space, and creates a set fro m the resulting list.
Pytho n remo ves the punctuatio n to ensure that o nly wo rds are present in the text. "Baa" is no t the same as "Baa," (with
a co mma), so the punctuatio n must be remo ved. The text is co nverted to lo wer case befo re splitting so that, fo r
example, "One" and "o ne" will no t be treated as unique wo rds. A set canno t co ntain duplicate entries. The number o f
elements in the set (given by the le n() functio n) is co mprised o f the number o f different wo rds in the text.
To see ano ther applicatio n o f sets, let's write a pro gram that co mpares two inputs and prints o ut the wo rds they have
in co mmo n and vario us o ther pieces o f info rmatio n. Type the co de belo w as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Find matching words in two input lines."""
Save it in yo ur /pyt ho n1 fo lder as wo rd_m at che r.py and run it, and then enter two different sentences with so me
wo rds in co mmo n, as sho wn:
INTERACTIVE SESSION:
cold1:~$ cd python1
cold1:~/python1$ ./word_matcher.py
Sentence 1: Four score and seven years ago
Sentence 2: Four and twenty blackbirds were baked in a pie
Words in both strings {'and', 'four'}
Unique to sentence 1: {'ago', 'seven', 'score', 'years'}
Unique to sentence 2: {'a', 'blackbirds', 'in', 'pie', 'twenty', 'were', 'baked'}
cold1:~/python1$
The pro gram prints the sets o f wo rds, telling yo u which are co mmo n to bo th sentences and which are unique to each
sentence. Because the sets are no t so rted, the pro gram prints them in unpredictable o rder. To o verco me this issue,
mo dify the pro gram to make use o f Pytho n's so rt e d() functio n. Edit yo ur co de as sho wn in blue:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Find matching words in two input lines."""
Save and run it, and enter the same two sentences. The o utput o f the first versio n o f this pro gram printed sets as its
results, but this mo dified versio n prints lists. When applied to a set, the so rt e d() functio n so rts the elements o f the set
into a list. This displays o ur results in a predictable (alphabetical) o rder.
INTERACTIVE SESSION:
In additio n to creating dicts with a literal representatio n, yo u can also add new key-value pairs, and replace the value
asso ciated with an existing key, using assignment statements. If yo u assign to an existing key in the dict, then the
assigned value replaces the previo usly asso ciated value. If no value is asso ciated with the key (in o ther wo rds, if the
key do es no t currently exist in the dict), then the key is added and the assigned value is asso ciated with the key.
Numeric keys receive slightly different treatment. Yo u might expect that d[1], d[1.0 ], and d[1+0 j] wo uld refer to
different values in the dict, but tho se three keys are all numerically equal, and so assigning to d[1.0 ] o verwrites the
value assigned to d[1], and the same value can be retrieved by referencing d[1+0 j].
Yo u can also see in o ur example that dicts have a ke ys() metho d that returns the keys o f the dict. This is kno wn in
Pytho n as an iterator. We'll lo o k at iterato rs in so me detail in a later co urse, but fo r the mo ment all yo u need to kno w is
that yo u can iterate o ver it, and each time aro und the lo o p, yo u get ano ther key fro m the dict. The same is true o f the
dict's it e m s() metho d, o nly this iterato r yields key-value pairs rather than the keys fro m the dict.
The dict is a flexible o bject type. Yo u can perfo rm the fo llo wing o peratio ns o n a dict d:
Remember, yo u learn mo re by experimenting. Play aro und with a dict o r two in an interactive co nso le until yo u are
co mfo rtable with the way they wo rk.
Because the dict is able to asso ciate a value with the key, we can use each wo rd as a key in the dict and have the
asso ciated value be the number o f times the wo rd appears in the text. Open yo ur wo rd_co unt e r.py pro gram, click
the Save As ( ) ico n, and save it in the /pyt ho n1 fo lder as a new file named wo rd_f re que ncy.py. Then, edit it as
sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Count the frequency of each word in a text."""
text = """\
Baa, baa, black sheep,
Have you any wool?
Yes sir, yes sir,
Three bags full;
One for the master,
And one for the dame,
And one for the little boy
Who lives down the lane."""
Save and run it. Yo u'll see o utput sho wing the number o f times each wo rd appears in the text. Wo rd splitting wo rks in
the same way as befo re, but no w, each time a wo rd is examined, the pro gram checks to find o ut whether the wo rd has
appeared befo re. If it has no t, then a new entry is made in the dict with a value o f o ne. If it has (if it is already fo und in the
f re q dict), the current co unt is incremented.
A slight mo dificatio n to the pro gram allo ws us to dispense with the if statement. Edit wo rd_f re que ncy.py as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Count the frequency of each word in a text."""
text = """\
Baa, baa, black sheep,
Have you any wool?
Yes sir, yes sir,
Three bags full;
One for the master,
And one for the dame,
And one for the little boy
Who lives down the lane."""
Save and run it. Yo u see the same results as befo re. This versio n o f the pro gram uses the same statement to update
the co unt, even if the wo rd has been seen befo re. It uses the ge t () metho d with a default value o f zero to retrieve the
existing co unt, so if the wo rd hasn't been seen befo re, the assignment inserts a value o f o ne against the new key.
CODE TO TYPE:
#!/usr/local/bin/python3
"""Count the frequency of each word in a text."""
text = """\
Baa, baa, black sheep,
Have you any wool?
Yes sir, yes sir,
Three bags full;
One for the master,
And one for the dame,
And one for the little boy
Who lives down the lane."""
Since we have to pro cess wo rds in pairs, we set f irst wo rd to be the first wo rd in the text. The lo o p then lo o ps o ver the
rest o f the text (t e xt wo rds[1:]), assigning the wo rd to ne xt wo rd. At the end o f each pass thro ugh the lo o p,
ne xt wo rd is assigned to f irst wo rd, so that at the start o f each iteratio n we have a co nsecutive pair o f wo rds in
f irst wo rd and ne xt wo rd.
The pro gram makes sure that there is an entry fo r the first wo rd in the wo rds dict: each entry starts o ut as an empty dict,
which will be used to sto re the number o f o ccurrences o f individual following wo rds. Then it uses the same technique
that the seco nd versio n o f wo rd_frequency.py did to update the co unt o f the following wo rd. The printing o f the o utput
has beco me a little mo re co mplex, because each wo rd requires yo u to print o ut each following wo rd. So in the o utput
phase, we have nested lo o ps: o ne lo o p inside ano ther.
Nice Work!
Yo u've just added sets and dicts to yo ur pro gramming to o l kit—no easy feat! Excellent! In the next lesso n, we'll fo cus
o n o utput, and ways to co ntro l the fo rmat o f the o utput pro duced by yo ur pro grams.
I like what I'm seeing so far! Keep it up and see yo u in the next lesso n...
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
String Formatting
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
Up until no w yo ur pro grams created o utput using the print () functio n, which simply sends the o utput to the screen witho ut
embellishment. But so metimes yo u'll need yo ur o utput to be fo rmatted in a particular way. Pytho n has so me really co nvenient
fo rmatting features that can help with that.
Let's lo o k at so me o f the capabilities that this o ffers. Start an interactive sessio n and enter the fo llo wing co mmands to
see ho w fo rmatting wo rks:
INTERACTIVE SESSION:
cold1:~$: python3
>>> "{2}, {1}, and {0}".format("George", "Paul", "John")
'John, Paul, and George'
>>> "{who} is a smart {what}".format(what='cookie', who='Sylvia')
'Sylvia is a smart cookie'
>>> "The fifth element of the first argument is {0[5]}".format(
... ["Dallas", "Zorg", "Cornelius", "Ruby", "Billy", "Leelo"])
'The fifth element of the first argument is Leelo'
>>> d = {'Cher': "Sarkisian", 'Sonny': "Bono"}
>>> "Sonny's surname is {0[Sonny]}".format(d)
"Sonny's surname is Bono"
>>> "Cher's surname is {lookup[Cher]}".format(lookup=d)
"Cher's surname is Sarkisian"
>>> for first, last in d.items():
... print("{0:10} {1:10}".format(first, last))
...
Cher Sarkisian
Sonny Bono
>>> fmt = "{0:>6} = {0:>#16b} = {0:#06x}"
>>> for i in 1, 23, 456, 7890:
... print(fmt.format(i))
...
1 = 0b1 = 0x0001
23 = 0b10111 = 0x0017
456 = 0b111001000 = 0x01c8
7890 = 0b1111011010010 = 0x1ed2
Yo u can see that Pytho n has so me very po werful string-fo rmatting capabilities. No w we need to understand the rules
and ways to write fo rmats that will give us the o utput we want. We'll go o ver these rules in the next few sectio ns.
The curly brackets play a vital ro le in fo rmatting strings. Each sequence o f characters surro unded by a pair o f curly
brackets is replaced by so me representatio n o f an argument to the f o rm at () metho d.
Function Arguments
The f o rm at () metho d, like all Pytho n functio ns, can be called with two types o f argument. The first type, and the o ne
yo u are mo st familiar with, is called positional, because it is identified by the po sitio n it o ccupies in the argument list.
The seco nd type is called keyword; it's preceded by a name and an equals sign.
If a call has any po sitio nal arguments, they must always appear befo re any keywo rd arguments. Thus, " ..." .f o rm at (a,
b, k1=c, k2=d) is legal, but " ..." .f o rm at (k1=c, k2=d, a, b) is no t (it will be flagged as a syntax erro r by the interpreter).
The arguments to the f o rm at () metho d call are the values to be fo rmatted. The fo rmat string o n which the metho d is
called specifies ho w the values are to be represented, by including replacement fields. Other text in the fo rmat string
(that do es no t appear between curly brackets) is simply co pied to the o utput literally.
To include actual curly brackets in the o utput, simply put two curly brackets to gether, {{ o r } } . These
Note do ubled curly brackets can never o ccur in a replacement field, and so they are treated specially.
These features alo ne can get yo u pretty far. Let's experiment no w and get mo re co mfo rtable pro gramming by writing a
slightly unusual pro gram. Usually we expect to pro vide variable data to a pro gram and fo rmat its results in a standard
way. This time we'll pro vide yo u with standard data and let yo u enter fo rmat specificatio ns that will select specific
elements fo r display.
CODE TO TYPE:
#!/usr/local/bin/python3
"""Accept format strings from the user and format fixed data."""
i = 42
r = 31.97
c = 2.2 + 3.3j
s = "String"
lst = ["zero", "one", "two", "three", "four", "five"]
dct = {"Jim": "Dandy",
"Stella": "DuBois",
1: "integer"}
while True:
fmt = input("Format string: ")
if not fmt:
break
fms = "{"+fmt+"}"
print("Format:", fms, "output:", fms.format(i, r, c, s, e=lst, f=dct))
Save it in yo ur /pyt ho n1 fo lder as f o rm at t ing.py and run it; verify that yo u get the answers sho wn fo r the inputs
given in the fo llo wing table:
To exit the pro gram, press Ent e r. The pro gram takes whatever yo u enter, wraps it inside curly brackets, and uses the
co nstructed string as a fo rmatting string against fo ur po sitio nal arguments and two keywo rd arguments. The print ()
call will o nly o utput a single value, but yo u can vary the fo rmat to get all kinds o f results.
If yo u do n't understand the results, yo ur instructo r can help cast so me light o n the to pic.
Format Specifications
The fo rmatting mechanism has so me pretty so phisticated ways to select what is fo rmatted. No w let's see abo ut
actually formatting the selected value. We do that by fo llo wing the field name with a co lo n and a format specification.
This can include details abo ut the filling mechanism to be used, ho w the o utput is to be aligned in the field, ho w to treat
the signs o f numbers, ho w wide the field sho uld be, ho w many digits o f precisio n to allo w, o r what type o f co nversio n
sho uld be perfo rmed o n the selected value.
The vario us co mpo nents o f the fo rmat specificatio n must appear in a prescribed o rder. No co mpo nent is required.
Alignm e nt
Me aning
Opt io n
The field is left-aligned in the available space, with any padding to its right. This is the default
<
when no alignment is specified.
> The field is right-aligned in the available space, with any padding to its left.
(Valid o nly fo r numeric types). Fo rces the padding to be placed after the sign but befo re any
= digits. This can be used to print padded numeric values with the signs all aligned abo ve each
o ther. Pad characters are typically "0 " o r "*".
The field is centered within the available space. Padding characters will be added o n the left
^
and right.
No padding is required if the value o ccupies the who le width o f the field. If no width is specified, this will
always be the case, and no padding will ever be inserted.
Sign
As yo u may have guessed, we do n't specify signs fo r no n-numeric values. The interpreter wo uld raise a
Value Erro r exceptio n if it fo und such a sign specificatio n. There are three ways we can use signs:
Opt io n Me aning
+ Insert a + sign fo r po sitive values, a - sign fo r negative values.
- Insert a - sign fo r negative values, no sign fo r po sitive values.
space Insert a - sign fo r negative values, a space fo r po sitive values
Base Indicator
The base indicatio n can o nly be requested fo r integers who se values are being displayed in hexadecimal,
o ctal, o r binary (simmer do wn, we're go ing to talk abo ut this stuff mo re in a few minutes). To request it,
include a hash mark (# ) in the fo rmat specificatio n. When a base indicato r is requested, binary numbers are
preceded by 0 b, o ctal numbers by 0 o and hexadecimal numbers by 0 x.
Digit Separator
To use co mmas as tho usands separato rs (fo r example, 9 ,9 9 9 ,9 9 9 ), insert a co mma in the fo rmat
specificatio n. This may restrict yo ur pro grams' po rtability, as so me lo cales use a co mma as a decimal po int
and a perio d as a tho usands separato r. To keep yo ur co de as po rtable as po ssible, use lo cale-dependent
types o f specificatio ns (mo re o n this in a few minutes).
Field Width
The field width is a decimal integer specifying the to tal width o f the o utput generated by the fo rmat specifier. As
a special case, if the field width begins with a zero character ('0 '), it is treated as a sho rthand fo r a pad
character o f '0 ' and a fill type o f '=' (zero es between the sign and the digits). This is illegal fo r no n-numeric
values and will raise a Value Erro r exceptio n under tho se circumstances.
Precision
Precisio n is specified as a perio d fo llo wed by a decimal number. Fo r numeric values, this indicates ho w
many significant digits to display:
INTERACTIVE SESSION:
>>> "{0:15.5}".format(987.654)
' 987.65'
>>> "{0:15.5}".format(98765.432)
' 9.8765e+04'
Fo r o ther types o f values, it indicates ho w many characters will be used fro m the field co ntent.
Field T ype
Last o f all co mes a letter that dictates which type o f value sho uld be fo rmatted. Fo r string values, the letter can
be o mitted, o r can be s. All numeric types can also be fo rmatted with a field type o f s, in which case the
resulting value befo re alignment and truncatio n (yeah, I said it: truncation—aka limiting the number o f digits
right o f the decimal po int) is the same as that pro duced by applying the built-in st r() co nversio n. Co mplex
number values canno t be fo rmatted in the same way as real and integer values; instead, yo u must fo rmat the
real and imaginary parts separately. Yo u can access these parts using the .re al and .im ag attribute qualifiers
in the field names. Integer and lo ng values can be fo rmatted with these field types:
Variable-Width Fields
The field width and the precisio n are numeric values. If yo u want these values to be reliant o n pro gram data, yo u can
pass the width and precisio n as arguments to the f o rm at () metho d and then use a nested field name inside the
fo rmat specificatio n. This nested field name (which must refer to an integer value) is substituted fo r the field width o r
precisio n as the fo rmatting takes place. So , fo r example, " {0 :{1} .{2} f } " .f o rm at (1234 .5 6 7 8, 18, 3) displays the
number 1234.56 78 to three decimal places in a field 18 characters wide.
Let's try a few examples. Start up an interactive sessio n and enter the co mmands sho wn:
INTERACTIVE SESSION:
>>> "{0:010.4f}".format(-123.456)
'-0123.4560'
>>> "{0:+010.4f}".format(-123.456)
'-0123.4560'
>>> for i in 1, 2, 3, 4, 5:
... "{0:10.{1}f}".format(123.456, i)
...
' 123.5'
' 123.46'
' 123.456'
' 123.4560'
' 123.45600'
>>> n = {'value': 987.654, 'width': 15, 'precision': 5}
>>> "{0[value]:{0[width]}.{0[precision]}}".format(n)
' 987.65'
The numerical ro unding is always co rrect. And by using dict access, yo u can carry the value, field width, and precisio n
(alo ng with o ther values yo u might need) all within a single o bject.
#!/usr/local/bin/python3
"""Produce a listing of people's names, ages and weights."""
data = [
("Steve", 59, 202),
("Dorothy", 49, 156),
("Simon", 39, 155),
("David", 61, 135)]
for row in data:
print("{0[0]:<12s} {0[1]:4d} {0[2]:4d}".format(row))
Save it in yo ur /pyt ho n1 fo lder as pe rso n_list .py and run it. While this pro gram wo rks, the co rrespo ndence
between related data items seems a little o bscure. Mo dify the pro gram as sho wn belo w to extract the individual items
fro m the ro w and pass them as separate arguments to the f o rm at () call:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Produce a listing of people's names, ages and weights."""
data = [
("Steve", 59, 202),
("Dorothy", 49, 156),
("Simon", 39, 155),
("David", 61, 135)]
for name, age, weight in data:
print("{0:<12s} {1:4d} {2:4d}".format(name, age, weight))
Save and run it again. The results are the same, but which co de do yo u think is easier to read?
Okay, no w let's make the name field wider. We'll use the perio d as a pad character to help the reader fo llo w the line
fro m the name to the age and weight. Mo dify the pro gram a third time—add a padding character befo re the alignment
indicatio n and increase the field width:
CODE TO TYPE:
#!/usr/local/bin/python3
#!/usr/local/bin/python3
"""Produce a listing of people's names, ages and weights."""
data = [
("Steve", 59, 202),
("Dorothy", 49, 156),
("Simon", 39, 155),
("David", 61, 135)]
for name, age, weight in data:
print("{0:.<30s} {1:4d} {2:4d}".format(name, age, weight))
In the next lesso n, we'll return to functio ns and talk abo ut the ro le o f keywo rd arguments and parameters like the o nes
we used with the string f o rm at () metho d in this lesso n.
See yo u there!
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
More About Looping
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
Earlier, yo u learned abo ut f o r and while lo o ps. The f o r lo o p repeats the lo o p bo dy o nce fo r each element o f a co ntainer. The
while lo o p repeats the lo o p bo dy co ntinuo usly, testing befo re each executio n whether so me co nditio n is true; it sto ps o nly
when the co nditio n beco mes false (o r a bre ak statement is executed, which terminates any lo o p). In this lesso n, we'll sho w
yo u so me o ther co o l things yo u can do with lo o ps.
INTERACTIVE SESSION:
Yo u just printed Pyt ho n is f un! o ne tho usand times, using the range () functio n, which generates arithmetic
pro gressio ns. When we ask the interpreter to print o ut the result o f a call to range (10 0 0 ), it do esn't print o ut a list o r a
tuple, as yo u might expect. In fact, range () returns a special type o f o bject kno wn as a range o bject. Yo u can iterate
o ver this o bject just like yo u can iterate o ver a list [0 , 1, 2, ..., 9 9 8 , 9 9 9 ]. Using the o bject is different fro m using a list
because it pro duces the numbers o ne by o ne as needed. This saves time and sto rage space that wo uld be needed to
co nstruct a list instead.
The last example printed a string co nstant. In this next example, we'll print o ut so me numbers:
INTERACTIVE SESSION:
Did yo u no tice that the 4 did no t print? The range starts at zero , so the given end po int is never part o f the generated
sequence. This may be co nfusing at first, but the interpreter has go o d reaso n fo r do ing it like that. Yo u'll see in this next
example:
INTERACTIVE SESSION:
(No thing perso nal, Ringo ; we just wanted to make a po int.) Generally, if yo u want to print o nly the names, yo u wo uldn't
lo o p o ver the indexes and use them to select the appro priate list elements. Instead yo u wo uld lo o p o ver the list
directly. If yo u ever see co de like f o r i in range (le n(so m e t hing)), that no rmally indicates what's so metimes called a
code smell. I kno w, funny, right? It's co de that wo rks, but still stinks a little. Co de smells are usually an indicatio n that
so mething needs to be changed.
By no w yo u can see that range () is a bit like indexing—it starts co unting at zero (unless yo u tell it to start so mewhere
else) and go es o n until just befo re it gets to the end value. What if yo u want a range o f numbers that starts at 5 and
ends at 7? Yo u give range () two arguments instead o f o ne:
INTERACTIVE SESSION:
Remember the stride we used with lists? We can do it in range () as well. Add a third argument as sho wn:
INTERACTIVE SESSION:
Yo u can also use a negative stride if yo u want a numerically descending sequence. In the next example, yo u'll see that
again, the sequence sto ps befo re it actually reaches the final value:
INTERACTIVE SESSION:
OBSERVE:
0 10
1 20
2 30
3 40
4 50
We can do that, right? We'll pro vide a co unter variable and increase it with each iteratio n:
INTERACTIVE SESSION:
>>> c = 0
>>> for i in range(10, 60, 10):
... print(c, i)
... c += 1
...
0 10
1 20
2 30
3 40
4 50
This metho d wo rks, but Pytho n gives us a better way: the functio n e num e rat e (). Like range (), it generates a
sequence o f values, but in this case, the values are tuples, each co ntaining two elements. The first element is a co unter
that starts at zero , and the seco nd element is the current item fro m the sequence that was given as an argument to
e num e rat e (). In a f o r lo o p, yo u can use a tuple o f two names to receive the elements, similar to the unpacking
assignments we used earlier. In the example belo w, i is the index and e is the element fro m the sequence:
INTERACTIVE SESSION:
No w we'll take a lo o k at two ways to print o ut a numbered list o f names. There's mo re than o ne way to do it; Pytho n
has an o lder way o f fo rmatting, no t deprecated, still wo rks, based o n the C language printf. Then it has a new
"fo rmatting mini-language" mo re like C#, intro duced with Pytho n 3 but also back-po rted to 2.6 and abo ve, using
numbers in {curly brackets} to identify o bjects to fo rmat, and the .fo rmat() functio n we learned abo ut in the last lesso n.
The fo rmat() versio n has mo re bells and whistles and makes it easier to do certain things. Also , o ne co uld argue it's
cleaner in no t requiring a separate o perato r. Here's an example using the o ld way:
INTERACTIVE SESSION:
INTERACTIVE SESSION:
Same results, different metho ds. We'll usually use the .fo rmat metho d in this co urse, but yo u're likely to enco unter the
%s metho d in the real wo rld, so we'll use it o ccasio nally.
In the abo ve examples, we add o ne to the co unt because, altho ugh Pytho n co unts fro m zero , we humans
Note no rmally prefer to start at o ne.
1! = 1 = 1
2! = 1 x 2 = 2
3! = 1 x 2 x 3 = 6
4! = 1 x 2 x 3 x 4 = 24
I'm abo ut to get mathematical and academic o n yo u fo r a minute here, so settle in. The textbo o k definitio n o f n factorial
(as lo ng as n is a no n-negative integer) is the pro duct o f all po sitive integers less than o r equal to n. Facto rials are
used in calculus, co mbinato rics, and pro bability theo ry.
Yo u could figure o ut all facto rials under 10 0 0 by figuring o ut the pattern o f calculatio ns manually, but that wo uld get
pretty tedio us with larger sets, do n't yo u think? In o rder to avo id that ago ny and its inherent po tential fo r erro rs, let's
use a while lo o p to reso lve the calculatio n instead. Create a new file in the edito r windo w as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Print all factorials less than 1000."""
c = 0
f = 1
while (f < 1000):
print(f)
c += 1
f = 1
for n in range(c, 0, -1):
f = f * n
Save it in yo ur /pyt ho n1 fo lder as f act o rial.py and run it. The pro gram prints all the facto rials under 10 0 0 .
There are actually two lo o ps here. The first (o r outer) lo o p uses the c variable to simply co unt upwards. The seco nd (o r
inner) lo o p generates the facto rial, based o n the value o f the co unter.
Each iteratio n o f the outer lo o p increments o ur co unter variable c by 1, co pies that to n, and resets the facto rial variable
f to 1. The inner lo o p do es its wo rk by taking the value o f n, which is simply a co py o f the co unter, and multiplying that
repeatedly against the facto rial variable.
So , if yo u already kno w N!, then yo u can pro duce (N+1)! (the next value in the sequence) by multiplying N! by N+1. Yo u
can make this pro gram even mo re efficient by avo iding the seco nd lo o p, since the seco nd lo o p wo uld be run fo r each
facto rial. This saves a lo t o f wo rk. Give it a try. Edit the co de as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Print all factorials less than 1000."""
c = 1
f = 1
while (f < 1000):
print(f)
c += 1
f *= c
Save and run it. The pro gram pro duces the same sequence o f values, but it do es no t repeat wo rk unnecessarily. This
beco mes mo re impo rtant as yo ur pro grams expand.
Suppo se we want to fo rce the user to pro vide a yes o r no respo nse. Create a new file in the edito r windo w as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Validate user input"""
while True:
s = input("Type 'yes' or 'no':")
if s == 'yes':
break
if s == 'no':
break
print("Wrong! Try again.")
print(s)
Save it in yo ur /pyt ho n1 fo lder as validat e _input .py and run it. The co nso le asks yo u to type yes o r no . Instead,
type spam and press Ent e r. The co nso le respo nds with Wro ng! T ry again.. If yo u enter anything besides ye s o r no ,
yo u'll get the same respo nse. When yo u finally enter ye s o r no , yo u break the While lo o p and yo ur entry is printed.
The pro blem with this pro gram is that it do esn't adapt itself well to mo re o ptio ns. Fo r example, if yo u need to add
m aybe as a po ssible respo nse, that invo lves adding two lines o f co de and mo difying a third. With yo ur
validat e _input .py in the edito r windo w, click the Save As ico n to save it in yo ur /pyt ho n1 fo lder as
be t t e r_validat e _input .py, and edit it as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Validate user input"""
Save and run it. In this new pro gram, the valid_input s list variable is used to build the string that queries the user fo r
input. It's also used to validate the user's input. So in o rder to add an o ptio n, yo u can just replace valid_input s =
['ye s', 'no ', 'm aybe '] with valid_input s = ['ye s', 'no ', 'm aybe ', 'another option'].
Mo re so phisticated user interface validatio n while lo o ps are used in applicatio ns such as o perating systems and web
site registratio n systems.
In the first example, yo u'll create a dictio nary using the wo rds o f the phrase Pyt ho n is awe so m e , using an
enumerated lo o p to do all the hard wo rk:
INTERACTIVE SESSION:
>>> data = {}
>>> for index, word in enumerate('Python is awesome'.split(' ')):
... data[index] = word
...
>>> print(data)
{0: 'Python', 1: 'is', 2: 'awesome'}
Keep this interactive sessio n o pen. First yo u created an empty dict, then enumerated o ver a list co ntaining the wo rds
split o ut o f the "Pytho n is aweso me" string. With each executio n o f the lo o p bo dy, yo u added the index o f the lo o p as a
dict key, using the wo rd as the value o f the dict element. Yo u can do this o ver any list o f data, fro m a list o f wo rds to
lines o f text in a file.
Our next example uses the it e m s() metho d fo r retrieving data fro m a dict. it e m s() returns a generato r o bject, which
then pro duces two -element tuples o f keys and their co rrespo nding values fro m the dict:
INTERACTIVE SESSION:
>>> data.items()
dict_items([(0, 'Python'), (1, 'is'), (2, 'awesome')])
>>> for element in data.items():
... print(element)
...
(0, 'Python')
(1, 'is')
(2, 'awesome')
>>> for key, value in data.items():
... print(key, value)
...
0 Python
1 is
2 awesome
The last two co mmands yo u entered are extremely useful, because they allo w yo u to access all the data in a dict
quickly. This is a very co mmo n pattern in wo rking with dicts in Pytho n. The dict's it e m s() metho d pro duces (key,
value) pairs, and the f o r lo o p unpacks the tuples and binds them to ke y and value , respectively.
Of co urse, there will be times when yo u'll need to remo ve key/value pairs fro m a dict. Suppo se yo u had a dict who se
keys were wo rds, and yo u wanted to remo ve all noise words (wo rds that are no t no rmally indexed, such as 'is' and
'at'). Yo u can use a lo o p to acco mplish this task:
INTERACTIVE SESSION:
The data was deleted, but the deletio n changed the size o f data.items, which Pytho n repo rts as an erro r. To avo id this
pro blem, yo u need to pro duce a separate list rather than iterating o ver the dict's items (o r keys) directly:
INTERACTIVE SESSION:
>>> data = {}
>>> for index, word in enumerate('Python is awesome'.split(' ')):
... data[index] = word
...
>>> for key, value in list(data.items()):
... if value in noise:
... del data[key]
...
>>> data
{0: 'Python', 2: 'awesome'}
We used the same techniques here that we used in earlier examples to lo o p thro ugh the key/values o f the dictio nary.
And in this new example, when o ne key/value matched o ne o f the listed prepo sitio ns, it deleted the element o f the dict
that co ntained that no ise wo rd. Can yo u think o f a data structure that wo uld have been better than a list to ho ld the
no ise wo rds?
#!/usr/local/bin/python3
invites = {}
options = ['add', 'list', 'approve', 'delete', 'quit']
prompt = 'Pick an option from the list (%s): ' % ', '.join(options)
status_1 = 'unapproved'
status_2 = 'approved'
while True:
inp = input(prompt)
if inp not in options:
print('Please pick a valid option')
continue
if inp == 'add':
name = input('Enter name:')
if not name:
continue
invites[name] = status_1
elif inp == 'list':
for name, status in invites.items():
print('%s (%s)' % (name, status))
elif inp == 'approve':
for name in invites:
if invites[name] == status_1:
break
else:
print('There must be %s status invites. Please pick another option' % statu
s_1)
continue
while True:
print('Please enter a valid name from the list below')
unapproved = []
for name in invites:
if invites[name] == status_1:
unapproved.append(name)
print(", ".join(unapproved))
name = input('Enter name:')
if not name:
break # user changed mind about approving
if name in unapproved:
invites[name] = status_2
print('%s %s' % (name, status_2))
break
elif inp == 'delete':
if not invites:
print('There must be invites before you delete any of them')
continue # user changed mind about deleting
while True:
print('Please enter a valid name from the list below')
for name, status in invites.items():
print('%s (%s)' % (name, status))
name = input('Enter name:')
if not name:
break
if name in invites:
del invites[name]
print('%s deleted' % name)
break
elif inp == 'quit':
print('Quitting invites')
print('The final invitation list follows')
for name, status in invites.items():
print('%s (%s)' % (name, status))
break
Save it in yo ur /pyt ho n1 fo lder as invit e .py and run it. The pro gram is really just o ne input validatio n lo o p that
checks to make sure that the user has entered o ne o f the five available co mmands. If the user has no t do ne this, the
pro gram repeats the request fo r input. Mo st o f the co mmands require further input, and each co mmand allo ws the user
to just press the Ent e r key to igno re the co mmand and request ano ther.
We used the %s metho d fo r fo rmatting o ur pro mpts here. Yo u sho uld be able to change the pro gram to
Note use the .fo rmat() metho d.
Loop This
We lo ve lo o ps because they let us repeat the same lo gic again and again as necessary. This means that yo ur
pro gram can execute so me pretty co mplex behavio rs, particularly when o ne lo o p co ntains o thers.
In the next lesso n, we'll learn ho w pro grams can use and sto re info rmatio n in files. See yo u there!
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Reading and Writing Files
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
create a file.
write to a file.
read files as text.
append to a file.
seek to arbitrary po sitio ns.
create a file-based to -do list.
read binary data.
So far, yo ur pro grams have used values that were either enco ded in the pro gram o r pro vided by the user, and the data
disappeared after the sessio n ended. But so metimes yo u'll need to use data fro m o utside so urces, o r to permanently save
data yo u've created o r mo dified in a pro gram. Pytho n has built-in functio ns to handle actio ns such as creating, writing, and
reading files. In this lesso n, yo u'll learn ho w to use files to create, retrieve, and change data.
INTERACTIVE SESSION:
cold1:~$ cd python1
cold1:~/python1$ python3
>>> f = open('funnies.txt', 'w')
>>> f
<_io.TextIOWrapper name='funnies.txt' encoding='UTF-8'>
This statement assigns the result o f the call to the o pe n() functio n to the variable f . The o pe n() functio n o pens a file
to read, write, o r append. The first argument gives the name 'f unnie s.t xt ' to the wo rking file (because we changed to
the /pyt ho n1 fo lder befo re we started the pytho n3 interpreter, this file will reside in that fo lder). The seco nd argument
'w' tells o pe n() that we want to writ e to the file.
When yo u ask the interpreter to display that variable, it displays the name o f the o pen file asso ciated with a particular
o bject. Right-click the /pyt ho n1 fo lder in yo ur File Bro wser o n the left, and select Re lo ad—it no w co ntains an empty
f unnie s.t xt file. (Do n't delete the file o r clo se the interactive co nso le because yo u'll need access to it in the next
sectio n!)
Note Depending o n yo ur enviro nment, yo u might see an enco ding type o ther than UTF-8 .
Writing to a File
Writing to a file is a go o d way to save info rmatio n. Pytho n pro vides two different ways to write to a text file. Let's return
to the interactive seesio n and take a lo o k:
INTERACTIVE SESSION:
The writ e () metho d adds the string (fo r example, 'Mo e \n') to the current co ntent o f yo ur funnies.txt file, and returns the
length o f the string added (fo r example, 4—including the \n as o ne character). The writ e line s() metho d takes a list o f
strings and adds each element to the funnies.txt file. Unlike the print () functio n, neither metho d adds newlines to the
co ntent it writes, so we include '\n' with each string so it will be o n a separate line in the file. The clo se () metho d
makes sure that all data is written o ut to the file and that the co nnectio n between it and the pro gram is dro pped.
Lo o k at f unnie s.t xt no w if yo u like—yo u'll see the six names yo u added. Be sure to clo se it befo re yo u
Note co ntinue with the lesso n.
INTERACTIVE SESSION:
Curly
Moe
Groucho
Chico
Harpo
>>> f.read()
''
>>> f.close()
No tice that we o pened the f unnie s.t xt file three times. That's because the file co ntent is "used up" by using the
re ad(), re adline (), and re adline s() metho ds, and we have to reo pen the file to return to the to p. re ad() returns all o f
the co ntent o f the file as a single string. re adline s() returns the file co ntent as a list o f lines. re adline () returns the
next line fro m the file, so when yo u called it o nce and then called re adline s(), the seco nd call returned a list that didn't
include the first line o f the file. The last metho d, f .re ad() returns no thing because the "po inter" is at the end o f the file.
When we're do ne with the file, we clo se it with the clo se () metho d. This releases reso urces that Pytho n was using to
lo o k at o r write to the file. So me pro grammers assume files will clo se auto matically at the end o f a pro gram, but it's
better pro gramming "hygiene" to clo se files when yo u finish using them.
Appending to a File
There are six lines o f text in yo ur funnies.txt file. Let's add so me mo re. If yo u o pen an existing file with the write o ptio n
w, yo u truncate the file's co ntents and pro duce an empty file—any new input will replace the file's o riginal co ntents. So
we don't use the write (w) o ptio n, using the append o ptio n (a) instead, as the seco nd argument to o pe n(). The next
example sho ws the append functio nality at wo rk. Enter these co mmands in yo ur interactive Pytho n co nso le:
INTERACTIVE SESSION:
>>> f = open('funnies.txt','a')
>>> f.write('A child of five could understand this. Fetch me a child of five.\n')
65
>>> f.write('Room service? Send up a larger room.\n')
37
>>> f.close()
>>> f = open('funnies.txt', 'r')
>>> for line in f:
... print(line[:-1])
...
Larry
Curly
Moe
Groucho
Chico
Harpo
A child of five could understand this. Fetch me a child of five.
Room service? Send up a larger room.
>>>
>>> f.close()
So , yo u o pened an existing file to append co ntent, and wro te in two new lines befo re clo sing it. When yo u o pened it
again, all o f the o ld co ntent was fo llo wed by the new co ntent yo u had just written. In this example, yo u used [:-1] to
slice each line to exclude the last character (the newline) fro m the end. This prevents yo ur co de fro m pro ducing the
blank lines that were printed o ut in the previo us example.
Value Me aning
Po sitio n is relative to the start o f the file (the first argument canno t be negative). This is the default if yo u
0
do n't supply an argument.
Po sitio n is relative to the current po sitio n (the first argument can be negative to mo ve backward o r po sitive
1
to mo ve fo rward).
2 Po sitio n is relative to the end o f the file (the first argument must be negative).
Yo u can't se e k() past the end o f a file. To find the current po sitio n in a file, call the t e ll() metho d.
INTERACTIVE SESSION:
>>> f = open('funnies.txt','a')
>>> f.name
'funnies.txt'
>>> f.readable()
False
>>> f.writable()
True
>>> f.seekable()
True
>>> f.encoding
'UTF-8'
>>> f.errors
'strict'
>>> f.closed
False
>>> f.close()
>>> f.closed
True
So phisticated persistence engines are called databases. Mo st o f the wo rld's data is sto red in databases. These are
integrated sets o f lo gically o rganized files o r reco rds. Databases can sto re text, integers, dates, images, and much
mo re. Usually, databases use the relatio nal mo del, but there are also hierarchical, o bject, netwo rk, and flat-file mo dels.
To demo nstrate the po wer o f persistence, and ways to take advantage o f it, we'll create a to -do list applicatio n as o ur
next example. Create a new file as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""File-based to-do list maintainer."""
otasks = open('open_tasks.txt','a')
otasks.close()
dtasks = open('done_tasks.txt','a')
dtasks.close()
options = ('add','done','quit')
string_input = 'Pick an option from the list (%s): ' % ', '.join(options)
while True:
open_tasks = open('open_tasks.txt','r').readlines()
if open_tasks:
print('-' * 10)
print('Open Tasks')
print('-' * 10)
for i, task in enumerate(open_tasks):
print(i, task.strip())
done_tasks = open('done_tasks.txt','r').readlines()
print('-' * 12)
print('Done Tasks')
print('-' * 12)
for i, task in enumerate(done_tasks):
print(i, task.strip())
inp = input(string_input)
if inp not in options:
print('Please pick a valid option')
continue
if inp == 'add':
new_task = input('Enter new task: ')
tasks = open('open_tasks.txt','a')
tasks.write(new_task + '\n')
tasks.close()
if inp == 'done':
while True:
done_task = input('Please enter the number of your completed task: ').strip
()
if done_task.isdigit():
done_task = int(done_task)
break
print('Please enter a task number')
open_tasks = open('open_tasks.txt','r').readlines()
for i, task in enumerate(open_tasks):
if i == done_task:
print('Task removed: {0}'.format(task))
open_tasks.remove(task)
f = open('open_tasks.txt','w')
f.writelines(open_tasks)
f.close()
f = open('done_tasks.txt','a')
f.write(task)
f.close()
break
if inp == 'quit':
break
Save it in yo ur /pyt ho n1 fo lder as t o do .py, and run it. Witho ut adding any tasks, type quit and press Ent e r. This
pro gram starts o ut by o pening each o f two data files to append, and then clo ses them immediately. This fo rces the
co mputer to create the files, in case this is the very first run. Right-click yo ur /pyt ho n1 fo lder in the File Bro wser at left
and select Re lo ad, and yo u'll see the two new files, o pe n_t asks.t xt and do ne _t asks.t xt .
No w, run the pro gram again and create a few tasks. Quit the pro gram and start it again. The tasks yo u added are still
there! And just like that, yo u've implemented a persistence engine that uses the flat file mo del! Open the files and yo u'll
see yo ur tasks have been added to o pe n_t asks.t xt .
Yo u used the string isdigit () metho d to make sure that yo ur user input fo r task numbers wo uld co nsist
Note o f numerical digits. This prevents the pro gram fro m raising exceptio ns when it co nverts the string to a
number with the int () functio n.
All the audio , image, and video files o n yo ur co mputer are binary files. So are any files co mpressed into zip o r tar
fo rmat. In fact, the majo rity o f pro grams o n yo ur co mputer—such as yo ur favo rite bro wser—are co mprised o f binary
files, the no table exceptio n being the Pytho n pro grams yo u are writing as part o f this co urse. And even tho se Pytho n
pro grams are co mpiled into binary fo rmat befo re the co mputer actually runs them!
No w that yo u have a backgro und in binary data and files, let's check o ut ho w Pytho n can handle a binary file. We'll use
wget to grab the image belo w:
cold1:~$ cd python1
cold1:~/python1$ wget "https://courses.oreillyschool.com/Python1/images/lessons/python-
logo.gif"
--2011-11-14 13:14:26-- https://courses.oreillyschool.com/Python1/images/lessons/pytho
n-logo.gif
Resolving courses.oreillyschool.com... 199.27.144.89
Connecting to courses.oreillyschool.com|199.27.144.89|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2549 (2.5K) [image/gif]
Saving to: "python-logo.gif"
100%[===============================================================>] 2,549 --.-K/s in
0s
2011-11-14 13:14:27 (26.8 MB/s) - "python-logo.gif" saved [2549/2549]
cold1:~/python1$ python3
>>> i = open('python-logo.gif', 'rb')
>>> i
<_io.BufferedReader name='python-logo.gif'>
>>> print(i.read(1))
b'G'
>>> print(i.read(1))
b'I'
>>> print(i.read(1))
b'F'
>>> i.read(10)
b'89a\xd3\x00G\x00\xf7\x00\x00'
>>> i.tell()
13
>>> i.seek(0)
0
>>> i.read(3)
b'GIF'
>>> i.close()
When yo u first lo o k at binary data, it can be pretty daunting. But even so , at a glance we can see a useful metho d and a
handy bit o f info rmatio n. The re ad() metho d fetches the byte(s) yo u request. Subsequent re ad() requests are fired
fro m yo ur current lo catio n in the file. The first three bytes pro vide the fo rmat o f the file yo u are examining (in this case,
GIF). This way a pro gram can figure o ut ho w to handle a file even if the file extensio n is missing. In fact, all mo dern
bro wsers check this info rmatio n befo re displaying images fo r yo u.
So , what abo ut the b'89 a\xd3\x0 0 G\x0 0 \xf 7 \x0 0 \x0 0 '? Well, that's part o f the image co ntent used to generate the
Pytho n lo go . Our example abo ve also sho ws the t e ll() and se e k() metho ds. se e k(0 ) "rewinds" the file to the
beginning.
Adding an integer argument to the re ad() metho d instructs yo ur pro gram to read the given number o f bytes. If there
aren't eno ugh bytes in the file, re ad(n) returns as many as there are. This means that if yo u get an empty sequence o f
bytes back, yo u are at the end o f the file.
Finally, the "strings" that yo u get when yo u read a file in binary mo de are what we call byte strings—each byte is eight
bits, so the ordinal value o f the characters is in the range 0 to 255. If yo u aren't familiar with the binary system, do n't
wo rry. Just be aware that Pytho n strings in binary differ fro m regular Pytho n strings in that yo u can represent pretty
much any character (as lo ng as yo ur character set includes it).
I'm thinking yo u're feeling pretty co nfident abo ut wo rking with files no w. But if yo u have any questio ns, go ahead and
ask yo ur mento r. They're here to help! Go o d jo b so far and see yo u in the next lesso n!
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Python's Built-In Functions
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
In every previo us lesso n, yo u've used so me o f Pytho n's built-in functio ns. The first built-in functio n yo u used was print () back
in lesso n 1. Since then yo u've used built-in functio ns like range (), o pe n(), and mo re. Built-in functio ns are an invaluable part o f
yo ur Pytho n to o l kit. In this lesso n, we'll learn even mo re abo ut them.
First we'll go o ver so me examples that use Pytho n built-in functio ns, and then explo re the functio ns themselves.
INTERACTIVE SESSION:
cold1:~$ python3
>>> invites = ["Jay", "Conan", "Jimmy", "Craig"]
>>> attendees = [3, 2, 0, 5]
>>> sum(attendees)
10
>>> zipped = zip(attendees, invites)
>>> party = tuple(zipped)
>>> party
((3, 'Jay'), (2, 'Conan'), (0, 'Jimmy'), (5, 'Craig'))
>>> max(party)
(5, 'Craig')
>>> for people, name in party:
... print(people, name)
...
3 Jay
2 Conan
0 Jimmy
5 Craig
>>> for people, name in sorted(party):
... print(people, name)
...
0 Jimmy
2 Conan
3 Jay
5 Craig
Keep the co nso le o pen. This example helps demo nstrate a co uple o f new functio ns: sum () returns the to tal o f all the
elements in its argument; zip() interleaves (that is, alternates, like the teeth o f a zipper) the elements o f any number o f
sequences. If yo u call zip() with two arguments, when yo u lo o p o ver the result yo u get a sequence o f two -element
tuples; call it with three arguments and yo u get three-element tuples. zip() do esn't return a list o r a tuple, but
so mething called a generato r; we called the t uple () functio n o n it so we co uld see the data witho ut needing to lo o p
o ver it.
Next, using the same data, let's check to see if any o r all invitatio ns have any attendees and the to tal number o f
invitatio ns. Type the co mmands belo w as sho wn:
INTERACTIVE SESSION:
>>> any(attendees)
True
>>> all(attendees)
False
>>> len(attendees)
4
The any() functio n returns T rue if any element o f its argument is true. all() returns T rue if all o f the elements o f its
argument are true. le n() returns the number o f elements in the argument.
The rest o f this lesso n pro vides an alphabetical reference guide to Pytho n's built-in functio ns, with brief examples. As
yo u beco me mo re familiar with Pytho n, yo u'll find new and inno vative ways to make use o f these built-in functio ns.
abs(x)
The abs() functio n returns the abso lute value o f an integer, flo ating po int, o r co mplex number. The returned value is
always po sitive. If the input value is a negative integer o r flo ating-po int number, then the abso lute value is the negated
argument. If the argument is co mplex, a po sitive result will still be returned, but it's a co mplicated calculatio n (the
square ro o t o f the sum o f the squares o f the real and imaginary co mpo nents). Take a lo o k. Type the co mmands belo w
as sho wn:
INTERACTIVE SESSION:
>>> abs(3.14)
3.14
>>> abs(-3.14)
3.14
>>> abs(3+4j)
5.0
all(iterable)
The all() functio n returns T rue if all elements o f the supplied iterable are true (o r if there are no elements: technically,
yo u co uld say it returns False if any element evaluates as false). So if all elements in a list, tuple, o r set match
Pytho n's definitio n o f being true, then all() returns T rue . Type the co mmands belo w as sho wn:
INTERACTIVE SESSION:
INTERACTIVE SESSION:
bool(x)
The bo o l functio n co nverts the value to a Bo o lean, using the standard Pytho n truth testing pro cedure. If x is false o r
o mitted, it returns False ; o therwise it returns T rue . Type the co mmands belo w as sho wn:
INTERACTIVE SESSION:
chr(i)
The chr() functio n returns a string o f o ne character, which has the o rdinal value equal to the given integer. Type the
co mmands belo w as sho wn:
INTERACTIVE SESSION:
>>> chr(90)
'Z'
>>> alphabet = ''
>>> for letter in range(65, 91):
... alphabet += chr(letter)
...
>>> alphabet
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
dict(arguments)
dict () creates a new data dictio nary with items taken fro m the arguments. If no arguments are passed, an empty
dictio nary is created. Yo u can call dict () with a tuple o r list as its argument. In tho se cases, each o f the argument's
elements must be a two -element (key, value) list o r tuple. Yo u can also use a sequence o f keyword arguments. We will
co ver tho se in the lesso n o n functio ns, but in sho rt, a keywo rd argument is a name fo llo wed by an equals sign and a
value. Try this example:
INTERACTIVE SESSION:
dir(arguments)
The dir() functio n can accept any argument: string, integer, dictio nary, functio n, class, o r metho d. Witho ut arguments,
dir() returns the list o f names in the current lo cal sco pe. If an argument is given, then the result is a list o f the names in
the namespace o f the given o bject. The list returned is always so rted in alphabetical o rder. Type the co mmands belo w
as sho wn:
INTERACTIVE SESSION:
>>> p = 'Python'
>>> dir(p)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format_
_', '__ge__', '_
_getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '_
_iter__', '__le__
', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__red
uce_ex__', '__rep
r__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__
', '_formatter_fi
eld_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'encode', 'endsw
ith', 'expandtabs
', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifi
er', 'islower', '
isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', '
lstrip', 'maketra
ns', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstri
p', 'split', 'spl
itlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
globals()
The glo bals() functio n returns a dictio nary representing the current glo bal symbo l table. This is always the
namespace dictio nary o f the current mo dule. Type the co mmands belo w as sho wn:
INTERACTIVE SESSION:
>>> list(globals().items())
[('__builtins__', <module 'builtins' (built-in)>), ('__name__', '__main__'), ('__doc__'
, None), ('__package__', None)]
>>> first = "Hello"
>>> second = "Goodbye"
>>> list(globals().items())
[('__builtins__', <module 'builtins' (built-in)>), ('__package__', None), ('second', 'G
oodbye'), ('__name__', '__main__'), ('__doc__', None), ('first', 'Hello')]
If yo u see mo re keys listed than are displayed in this example, it's pro bably because yo u've been trying
Note different snippets o f co de.
help(object)
The he lp() functio n is yo ur new best friend. Invo ke the built-in help system o n any o bject and it will return usage
info rmatio n o n the o bject. Fo r experienced Pytho n pro grammers, this is the first to o l to use when trying to figure o ut
so mething they do n't understand. Once yo u start writing mo re advanced Pytho n pro grams, yo u'll learn ho w to write
yo ur o wn help text.
In an interactive Pytho n co nso le, use the he lp(object) functio n o n any variable, string, integer, list, tuple, set, o r built-in
functio n, including the he lp() functio n. So me o f the text wo n't make sense to yo u right no w, but yo u'll still find this
functio n very useful. To scro ll thro ugh larger help do cuments, press the space bar. To exit, press q. Type the
co mmands as sho wn:
INTERACTIVE SESSION:
>>> help(globals)
Help on built-in function globals in module builtins:
globals(...)
globals() -> dictionary
len(...)
len(object) -> integer
len(s)
le n(s) returns the length o f an o bject. The argument pro vided may be a sequence (string, tuple, o r list) o r a mapping
(dictio nary). Type in these co mmands:
INTERACTIVE SESSION:
>>> s = "Python"
>>> len(s)
6
>>> lst = [1, 2, 3]
>>> len(lst)
3
>>> d = {"a":"b", "c":"d", "e":"f"}
>>> len(d)
3
locals()
The lo cals() functio n returns a dictio nary representing the current lo cal symbo l table. Unless it's called inside a
functio n, it will return the same list as glo bals(). Type in this co mmand:
INTERACTIVE SESSION:
>>> locals()
{'__builtins__': <module 'builtins' (built-in)>, '__name__': '__main__', '__doc__': Non
e, '__package__': None}
Just like the glo bals() functio n, yo u will likely see mo re keys than we sho w in this example. That's
Note perfectly fine; it means yo u've pro bably been testing different snippets o f co de. Go o d fo r yo u!
max(iterable)
The m ax() functio n, with a single argument iterable, returns the largest item o f a no n-empty iterable (such as a string,
tuple, o r list). With mo re than o ne argument, it returns the largest o f the arguments. Type these co mmands:
INTERACTIVE SESSION:
Do n't clo se the sessio n—we'll use lst1 and lst2 with the next functio n! The first result is 6 4. The seco nd result o f 'two '
might have surprised yo u, but Pytho n co mpares strings "lexico graphically" (like they wo uld be so rted fo r a dictio nary,
but with all the lo wer-case letters greater than any upper-case o ne), no t by the meaning o f the wo rds. The last
expressio n caused an erro r, because yo u can't co mpare strings and integers: they are fundamentally different types.
min(iterable)
The o ppo site o f the m ax() functio n, m in(it e rable ) returns the smallest item o f a no n-empty iterable (such as a string,
tuple, o r list). With mo re than o ne argument, it returns the smallest o f the arguments. Type these co mmands:
INTERACTIVE SESSION:
>>> min(lst1)
2
>>> min(lst2)
'One'
>>> min(42, 76, -104)
-104
>>> min(1, 2, 'three')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: str() > int()
The first result is 2. The seco nd result o f 'One' seems to make sense, but be aware that Pytho n returned the lo west
value o f an alphanumeric so rt, "O" being less than "T": Pytho n neither kno ws no r cares abo ut the meaning o f the
wo rds. The fo urth expressio n raised an exceptio n here as well, because yo u can't co mpare strings and integers.
ord(c)
o rd(c) is the inverse o f the chr() functio n we discussed earlier. Given a string o f length o ne, it returns an integer
representing the o rdinal value o f the character. Fo r example, o rd('A') returns the integer 6 5. Type these co mmands:
INTERACTIVE SESSION:
Yo u might have played with a calculato r at o ne time o r ano ther, using repeated multiplicatio n to raise a number to
successive po wers. In the next example, Pytho n auto mates the calculatio ns fo r yo u. Type these co mmands:
INTERACTIVE SESSION:
>>> pow(2, 2)
4
>>> pow(2, 3)
8
>>> pow(2, 4)
16
>>> for i in range(5, 12):
... print(pow(2, i), pow(2, i, 100))
...
32 32
64 64
128 28
256 56
512 12
1024 24
2048 48
reversed(seq)
re ve rse d(se q) is a reverse iterato r o n an o bject o f the type that yo u can lo o p thro ugh and pro cess. The list and
t uple types are suppo rted with this functio n, but the se t type is no t (because the elements o f a set aren't o rdered).
Type these co mmands:
INTERACTIVE SESSION:
INTERACTIVE SESSION:
>>> round(33.5)
34
>>> round(33.3333333333, 2)
33.33
sorted(iterable)
so rt e d(it e rable ) returns a new so rted list fro m the items in iterable. This arranges yo ur lists, tuples, and sets in a
kno wn o rder. Type these co mmands:
INTERACTIVE SESSION:
The first so rted list pro vides an expected result. The seco nd list yo u may no t have anticipated. Pytho n so rts in
alphanumeric o rder, but all upper-case letters so rt lo wer than all lo wer-case letters.
Yo u can also use keywo rd arguments to specify ho w the so rt keys sho uld be created, and whether to so rt in
ascending o r descending o rder. Suppo se yo u want to have a case-insensitive search. Yo u can do this by using a
functio n as the ke y argument o f the so rt. In this case, yo u use the Pytho n string type's lo wer-case metho d. In the
seco nd example, yo u request a descending so rt with the re ve rse keywo rd argument. Type these co mmands:
INTERACTIVE SESSION:
>>> t = ['Beta','beta','alpha','Alpha']
>>> sorted(t, key=str.lower)
['alpha', 'Alpha', 'Beta', 'beta']
When yo u use the lo we r() functio n o n o therwise identical strings like 'Beta' and 'beta', Pytho n treats
Note them as identical, keeping them in the same o rder they were input, so 'beta' might no t appear befo re
'Beta' when yo u try the abo ve example.
INTERACTIVE SESSION:
>>> t = ['Bete','beta','alphie','Alpha']
>>> sorted(t, key=str.lower)
['Alpha', 'alphie', 'beta', 'Bete']
>>> sorted(t, reverse=True)
['beta', 'alphie', 'Bete', 'Alpha']
sum(iterable)
sum (it e rable ) sums the numeric values in an iterable such as a list, tuple, o r set. sum (it e rable ) do es no t wo rk with
strings because yo u can't do math o n strings (when yo u add two strings yo u are really using an o peratio n called
concatenation). Type these co mmands:
INTERACTIVE SESSION:
>>> s = {1, 2, 3}
>>> sum(s)
6
>>> lst = ['Python','is','fun!']
>>> sum(lst)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
So we are able to add up numbers, but things break do wn o n letters and wo rds. To co mbine strings in a list, rely o n the
string jo in() metho d as sho wn belo w:
INTERACTIVE SESSION:
zip(*iterables)
The zip() functio n takes iterables and aggregates elements fro m each o f the iterables into a new iterable o bject. That
might so und co mplicated, but the example belo w will help illustrate the co ncept. Type these co mmands:
INTERACTIVE SESSION:
In the first result, we used the list () functio n to create a list o f three tuples. The seco nd example is no t so clear, as we
are missing the last two elements o f lst _3. That's because the zip functio n igno red iteratio ns fo r which it didn't have
elements in all o f the supplied iterables. This enables us to create dicts using the zip() functio n. Try it o ut:
INTERACTIVE SESSION:
Keep yo ur interpreter windo w o pen to test yo ur understanding o f new functio ns as yo u co me into co ntact with them.
Experiment and try to find their limits. Use the he lp() functio n to learn mo re abo ut the built-in functio ns to o .
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Defining and Calling Your Own Functions
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
Exploring Functions
Hi and welco me to a new lesso n! We're mo ving right alo ng, huh? Let's keep it go ing. While wo rking thro ugh examples
so far in this co urse, yo u typed similar pieces o f co de o ver and o ver again. Take a lo o k at this snippet o f co de fro m o ur
earlier complex file handling example:
OBSERVE:
open_tasks = open('open_tasks.txt','r').readlines()
if open_tasks:
print('-' * 10)
print('Open Tasks')
print('-' * 10)
for i, task in enumerate(open_tasks):
print(i, task.strip())
done_tasks = open('done_tasks.txt','r').readlines()
print('-' * 10)
print('Done Tasks')
print('-' * 10)
for i, task in enumerate(done_tasks):
print(i, task.strip())
Yo u typed in almo st exactly the same co de twice. Wo uldn't it be nice if yo u co uld just write it o nce and then call it
whenever yo u needed it, like yo u've do ne with Pytho n's vario us built-in functio ns? Fo rtunately, yo u can! Lo o k at the
same co de, this time rewritten using a functio n:
OBSERVE:
def task_report(task_file):
tasks = open(task_file,'r').readlines()
if tasks:
print('-' * 10)
print(task_file.replace('_',' ').replace('.txt','').title())
print('-' * 10)
for i, task in enumerate(tasks):
print(i, task.strip())
task_report('open_tasks.txt')
task_report('done_tasks.txt')
The seco nd example defines a functio n t ask_re po rt . It executes the same task as the first example, but o perates by
calling the functio n twice. The differences between the two examples are in the file name and the heading that was
printed o ut. The file name is a formal parameter (t ask_f ile ) o f the functio n, and the heading is created by using
re place () to change the undersco re ('_') to a space (' ') and the extensio n ('.t xt ') to no thing ('') in the file name, and
then applying title case to the remainder with t it le (). (The net result? 'o pen_tasks.txt' beco mes 'Open Tasks' and
'do ne_tasks.txt' beco mes 'Do ne Tasks.')
By defining and using this functio n, we saved three who le lines o f co de. Impressed? No ?! Yo u sho uld be. But wait,
there's mo re!
No w, suppo se yo u need to add three mo re types o f task state to yo ur co de: "no t yet co nfirmed," "in testing," and
"under review." Witho ut o ur task_repo rt functio n, we wo uld have had to add 18 lines o f co de to acco mplish this task!
But using the functio n, we can pro cess each file with a single line, and get the jo b do ne with just three lines o f co de!
Take a lo o k at the example:
Go o d Pytho n develo pers never repeat a stanza o f co de twice. Instead, we put it into a functio n, and call that functio n as
o ften as we need it. If yo u see lo ts o f repetitive co de, it generates that yucky code smell I mentio ned earlier. The co de
might wo rk, but changing it, maintaining it, and using it in o ther places will be harder than it needs to be and the co de
will be mo re pro ne to erro rs.
CODE TO TYPE:
#!/usr/local/bin/python3
def average(lst):
""" Averages a list, tuple, or set of numeric values"""
return sum(lst) / len(lst)
tst_lst = [1, 2, 3, 4]
print('Average this list: {0}'.format(tst_lst))
print(average(tst_lst))
t = (243, 132, 987, 342, 13)
print('Average this tuple: ',t)
print(average(t))
s = {1, 2, 3, 4, 25}
print('Average this set: {0}'.format(s))
print(average(s))
INTERACTIVE SESSION:
cold1:~$ cd python1
cold1:~/python1$ ./average.py
Average this list: [1, 2, 3, 4]
2.5
Average this tuple: (243, 132, 987, 342, 13)
343.4
Average this set: {25, 2, 3, 4, 1}
7.0
Ho w do es it wo rk?
OBSERVE:
#!/usr/local/bin/python3
def average(lst):
""" Averages a list, tuple, or set of numeric values"""
return sum(lst) / len(lst)
tst_lst = [1, 2, 3, 4]
print('Average this list: {0}'.format(tst_lst))
print(average(tst_lst))
t = (243, 132, 987, 342, 13)
print('Average this tuple: ',t)
print(average(t))
s = {1, 2, 3, 4, 25}
print('Average this set: {0}'.format(s))
print(average(s))
The functio n o ccupies o nly the first three lines o f co de in this example. The Pytho n keywo rd de f intro duces a functio n
definitio n. It must be fo llo wed by the f unct io n nam e and the list o f f o rm al param e t e rs in parentheses. The co de
that makes up the functio n is called the function body. The functio n bo dy must be indented. The string " " " Ave rage s a
list , t uple , o r se t o f num e ric value s" " " is the functio n's do cumentatio n string, o ften abbreviated as docstring. The
interpreter uses do cstrings to give pro grammers info rmatio n abo ut ho w the functio n wo rks and ho w it sho uld be
called. Finally, the last line tells the functio n to re t urn the sum () o f the values entered, divided by the number o f values
as determined by the le n() functio n. This returned value beco mes the value o f the functio n call during evaluatio n o f
expressio ns.
Our functio n is fo llo wed by test co de that lets us verify that the functio n wo rks co rrectly. Each time the ave rage ()
functio n is written with a list o f numbers in it, such as ave rage (t st _lst ) o r even ave rage ([10 ,20 ,30 ,4 0 ,5 0 ]), o ur
functio n co de is run with tho se numbers as fo rmal parameters.
In the average.py example, we used the name lst fo r o ur parameter. It co uld have any name, but fo r the sake o f clarity,
use a name that makes the purpo se o f the variable clear. Also , be careful no t to use names o f existing Pytho n
functio ns o r o ther o bjects. Yo u do n't want to use list o r t uple as variable o r parameter names, fo r example, because
they are the names o f Pytho n built-in functio ns. If yo u do , yo ur pro gram may behave in co mpletely inco mprehensible
ways.
Suppo se yo u want to write a functio n that prints o ut the elements in a list, and yo u want to pro vide an o ptio n to have
the functio n print the list in reverse o rder. To do this, yo u'll use positional and keyword parameters. Type the co de as
sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
def print_list(lst, rev=False):
""" prints the contents of a list. """
if rev:
lst = reversed(lst)
for i in lst:
print(i)
Save it in yo ur /pyt ho n1 fo lder as print _list .py, and run it. This functio n takes two parameters. Yo u're familiar with
the first parameter, lst ; the seco nd parameter, re v=False , intro duces a new feature—a keywo rd parameter, which has
a default value (the value fo llo wing the equals sign, which in this case is False ). If yo u call the functio n witho ut passing
an argument co rrespo nding to the re v parameter, it uses that default value.
The functio n's co de lo o ks at the value o f re v, and if it is true, it re-binds the parameter to a reversed co py o f the list. It
do es this rather than reversing the list in place, because such a reversal wo uld affect co de o utside o f the functio n
(tho ugh there's no thing illegal abo ut changing a mutable o bject inside o f a functio n, yo u want to make sure that the
users o f the functio n kno w they sho uld expect such changes. We'll go o ver parameters and arguments in greater detail
in future lesso ns).
Returning Values
The first functio n yo u wro te in this lesso n, ave rage (), returned a value that yo ur co de then displayed via the built-in
print () functio n. When a functio n call is written in an expressio n (fo r example, in print (ave rage (t st _lst ))), the value
o f the functio n call in that expressio n is actually the value that the functio n returned in its re t urn statement (2.5). But the
seco nd functio n yo u created, print _list (), did no t include a re t urn statement. This is equivalent to the functio n ending
with re t urn No ne . So all functio ns will return so me value, but by co nventio n, functio ns that do n't need to return
anything can implicitly return No ne . If the functio n isn't intended to return a value, it's co nfusing to add an explicit
re t urn statement.
Yo u can either use the functio n calls in co ntro l flo w co de (that is, co de that co ntro ls the o rder in which tasks are
executed, such as if o r while statements) o r save the values returned by functio ns, binding them to a variable in an
assignment statement and using that value again and again witho ut needing to rerun the functio n. To see these
principles in actio n, create a new file as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
def structure_list(text):
"""Returns a list of punctuation in a text"""
punctuation_marks = "!?.,:;"
punctuation = []
for mark in punctuation_marks:
if mark in text:
punctuation.append(mark)
return punctuation
text_block = """\
Python is used everywhere nowadays.
Major users include Google, Yahoo!, CERN and NASA (a team of 40 scientists and engineer
s
is using Python to test the systems supporting the Mars Space Lander project).
ITA, the company that produces the route search engine used by Orbitz, CheapTickets,
travel agents and many international and national airlines, uses Python extensively.
The YouTube video presentation system uses Python almost exclusively, despite their
application requiring high network bandwidth and responsiveness.
This snippet of text taken from chapter 1"""
Save it in yo ur /pyt ho n1 fo lder as re t urn_value .py, and run it. The st ruct ure _list () functio n accepts a single
parameter called text. This value is checked to find co mmo n punctuatio n marks. These results are placed into a list and
that list is returned.
The tricky part is the lo o p itself and what it do es with the returned value o f st ruct ure _list (). Instead o f immediately
printing the value, we save it to the variable p. This variable is subsequently used in two different if statements. The first
checks to see if the list p is empty, then prints an appro priate result. Then the variable is used again to determine
whether o r no t a co mma is present.
CODE TO TYPE:
#!/usr/local/bin/python3
def structure_list(text):
"""Returns a list of punctuation and the location of the word 'Python' in a text"""
punctuation_marks = "!?.,:;"
punctuation = []
for mark in punctuation_marks:
if mark in text:
punctuation.append(mark)
return punctuation, text.find('Python')
text_block = """\
Python is used everywhere nowadays.
Major users include Google, Yahoo!, CERN and NASA (a team of 40 scientists and engineer
s
is using Python to test the systems supporting the Mars Space Lander project).
ITA, the company that produces the route search engine used by Orbitz, CheapTickets,
travel agents and many international and national airlines, uses Python extensively.
The YouTube video presentation system uses Python almost exclusively, despite their
application requiring high network bandwidth and responsiveness.
This snippet of text taken from chapter 1"""
Save and run it. We mo dified the functio n to return a two -element tuple. The first element is the punctuatio n as
co mputed in the previo us versio n. The seco nd element is the lo catio n o f the wo rd "Pytho n." If the wo rd do esn't exist in
the text, -1 is returned, as determined by the f ind() metho d's specificatio n.
The functio n result is assigned to two separate variables using an unpacking assignment, and an additio nal test is
made o n the returned index value to determine whether to repo rt the presence o f the wo rd "Pytho n."
When yo u call a functio n, Pytho n dynamically creates a new namespace and binds the argument values to the
appro priate parameter names. Assignments made during executio n o f the functio n call result in bindings in the functio n
call namespace. When the functio n returns, the namespace is auto matically destro yed, and any bindings inside the
namespace are lo st.
Yo u can sum up ho w functio ns handle namespaces in Pytho n by understanding these two rules:
1. Variables bo und within a Pytho n functio n bo dy o nly exist in namespaces created by calls o f that functio n.
2. Variables bo und in the glo bal namespace can be accessed by functio ns, but may no t be bo und unless
specifically declared to be glo bal.
Let's test o ut the first rule. As yo u will see, the variable c defined belo w is assigned inside o f the t e st () functio n. Start
an interactive sessio n and enter the co mmands sho wn:
INTERACTIVE SESSION:
And no w let's test the seco nd rule. Type the co mmands belo w as sho wn:
INTERACTIVE SESSION:
Yo u can see that when the functio n attempts to access a glo bal variable a, the functio n fails in its first call, because a
has no t yet been created in the glo bal enviro nment. The interpreter kno ws that a is no t lo cal to the functio n because the
functio n bo dy co ntains no assignment to it. Once the variable is created by an assignment in the mo dule namespace,
a call to the functio n succeeds witho ut raising an exceptio n.
So , if any assignment is made to a variable inside a functio n bo dy, the variable is lo cal to the functio n. Changing a
glo bal variable inside a functio n bo dy isn't a best practice, but so metimes it's a necessary evil. To achieve that end,
yo u use a glo bal statement to declare that the variable, altho ugh assigned inside o f the functio n bo dy, is in the
mo dule (glo bal) sco pe. To demo nstrate this, type the co mmands belo w as sho wn:
INTERACTIVE SESSION:
Here the value " Pyt ho n" is bo und to a in mo dule sco pe. After the functio n is called, yo u can see that a has been re-
bo und by the assignment inside o f the functio n.
When yo u prefix the parameter with the asterisk (*) character in the functio n definitio n, this tells the interpreter to co llect
any unmatched po sitio nal arguments into a tuple and then bind the tuple to the name fo llo wing the asterisk in the
called functio n's namespace. Inside o f yo ur functio n, this tuple can be used like any o ther Pytho n iterable. Let's check it
o ut. Create a new file in the edito r windo w as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
def multiplier(*args):
""" Multiply the arguments together and return the result.
Return 0 if nothing is provided.
"""
if not args:
return 0
product = args[0]
for a in args[1:]:
product *= a
return product
print(multiplier())
print(multiplier(1,2,3,4))
print(multiplier(6,7,8,9,10,11,12,13))
print(multiplier(10,20,100))
Save it in yo ur /pyt ho n1 fo lder as argum e nt _list .py, and run it. The m ult iplie r() functio n, o ur single parameter
args (which yo u can think o f as the "sequence parameter") is prefixed with *, so all po sitio nal arguments to a call will
appear inside o f this tuple. The rest o f the functio n is made up o f familiar co de (if anything is unfamiliar, ask yo ur
instructo r fo r a little help). We can call the functio n with any number o f arguments.
The * sequence parameter must fo llo w any standard po sitio nal o r keywo rd parameters. This can be useful when
regular arguments are also required. Fo r instance, yo u may want to pro vide an o ptio nal amo unt to be added to the
pro duct. Yo u'd acco mplish that by using a keywo rd argument with a default value o f zero . Let's see ho w this is do ne.
Mo dify the pro gram as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
def multiplier(total=0.0, *args):
""" Multiply the arguments together, add a prior total, and return the result.
Return 0 if nothing is provided.
"""
if not args:
return total
product = args[0]
for a in args[1:]:
product *= a
print("product:", product)
return product + total
print(multiplier())
print(multiplier(1,2,3,4))
print(multiplier(6,7,8,9,10,11,12,13))
print(multiplier(10,20,100))
Save and run it. The first parameter o f each set is no w passed as the t o t al, and the rest as args.
To illustrate, we will use the built-in input () metho d to pro mpt fo r two numeric values. The co de do es multiplicatio n
the o ld way (6 + 6 + 6 + 6 + 6 + 6 + 6 ). Finally, we'll use the ability o f functio ns to use the glo bal namespace to cache
the results, so when yo u try it with big numbers (10 millio n * 10 millio n), yo u do n't need to repeat lengthy calculatio ns.
In the co de example belo w, we create a kid() functio n to do the math. Rather than intro duce so me ho rrendo usly
co mplicated functio n that wo uld be difficult to understand, we'll use a mo re manageable functio n. kid() do es
multiplicatio n the hard way! Create a new file in the edito r windo w and type the co de sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
""" Demonstrates the need for caching """
while True:
a = input('enter a number: ')
b = input('enter another number: ')
a = int(a)
b = int(b)
print(kid(a,b))
Save it in yo ur /pyt ho n1 fo lder as caching.py, and run it. Try it with small numbers first, perhaps 4 and 5. Then try
so mething large like 5 and 10 0 0 0 0 0 0 (o ne and seven zero s). Yo u'll have to wait awhile as the co mputer adds 5 ten
millio n times. That crazy kid takes fo rever to do basic math!
No w, mo dify yo ur kid() functio n so that it maintains a reco rd o f the arguments it has been called with, and saves
previo usly-co mputed results in a glo bal dict so that befo re it even starts to perfo rm a calculatio n, it can pro vide a
previo usly-co mputed result, thereby saving time. Edit the co de belo w as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
""" Demonstrates the need for caching """
global_cache = {}
c = 0
for i in range(b):
c += a
global_cache[(a, b)] = c
return c
while True:
a = input('enter a number: ')
b = input('enter another number: ')
a = int(a)
b = int(b)
print(kid(a,b))
print(global_cache)
print('-'*40)
No w try the pro gram again. Enter 5 * 10 0 0 0 0 0 0 . Wait a few seco nds fo r the respo nse and try it again. Yo u'll no tice the
seco nd time it returns almo st instantly.
Here, when the functio n is called, it immediately checks the glo bal glo bal_cache dict to see whether this particular set
o f arguments has been used befo re. If it has, the cached result is immediately returned, bypassing the lengthy
co mputatio n. If the argument set isn't fo und in glo bal_cache , then it is co mputed in the usual way, but befo re the
result is returned, it is added to the glo bal_cache so this new result can be pro duced immediately if we ever need it
again.
A Solid Foundation
In this lesso n, yo u started to learn ho w to write functio ns, understand the difference between parameters and
arguments, ho w return values wo rk, and a little mo re abo ut namespaces. I'm really impressed with yo ur pro gress so
far! No w that yo u have a pretty go o d grip o n Pytho n basics, let's mo ve o n and learn abo ut mo dules and impo rts, and
even mo re abo ut namespacing.
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
The Python Standard Library
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
Increased Versatility
Let's get to wo rk and discuss a key co ncept in pro gramming, the principle of modularity. The idea behind it is that
unrelated parts o f a system sho uld be kept separate fro m each o ther, and related parts sho uld be gro uped to gether.
Pytho n co mes with a large set o f library mo dules and packages (we'll talk mo re abo ut packages in a later co urse—
they're like mo dules, but with a bit mo re structure). It's well wo rth learning abo ut the standard library because it
co ntains mo dules that can save yo u time and effo rt, and also allo w yo u to do so me really co o l stuff with Pytho n.
Let's try a few experiments using the standard library. First we'll figure o ut ho w to impo rt a Pytho n library mo dule. Type
the co mmands sho wn belo w in an interactive sessio n:
INTERACTIVE SESSION:
cold1:~$ python3
>>> import textwrap
>>> textwrap.wrap("This is a very long piece of text. This should appear as shorter lin
es.", 12)
['This is a', 'very long', 'piece of', 'text. This', 'should', 'appear as', 'shorter',
'lines']
>>> import time
>>> time.time()
1327529513.914024
>>> time.gmtime()
time.struct_time(tm_year=2012, tm_mon=1, tm_mday=25, tm_hour=22, tm_min=12, tm_sec=7, t
m_wday=2, tm_yday=25, tm_isdst=0)
>>> time.asctime(time.gmtime())
'Wed Jan 25 22:13:04 2012'
>>> import base64
>>> base64.encodestring(b"This is a byte string")
__main__:1: DeprecationWarning: encodestring() is a deprecated alias, use encodebytes()
b'VGhpcyBpcyBhIGJ5dGUgc3RyaW5n\n'
>>> s = base64.encodebytes(b"This is a byte string")
>>> base64.decodestring(s)
__main__:1: DeprecationWarning: decodestring() is a deprecated alias, use decodebytes()
b'This is a byte string'
>>> base64.decodebytes(s)
b'This is a byte string'
Here yo u made use o f functio nality fro m three standard library mo dules—textwrap, time, and base6 4. We have linked
the name o f each mo dule to the appro priate sectio n o f Pytho n's standard library do cumentatio n. Yo u get access to the
reso urces o f a mo dule by qualifying the mo dule's name with the name o f the appro priate reso urce. So "a.b" means
"lo o k in a's namespace and return what is bo und to the name b there."
The Deprecatio nWarning message is in o ur co de to remind tho se pro grammers using earlier versio ns o f Pytho n that
o ur strings are no w Unico de. In o lder versio ns, strings were by default made up o f ASCII (8 -bit) characters. In Pytho n 3,
the base 6 4 .e nco de st ring() functio n has been renamed base 6 4 .e nco de byt e s(). The o ld name is still available,
but no t fo r lo ng, so a message is printed to alert pro grammers to use the newer name.
Namespaces
Earlier, we discussed Pytho n's object space, the lo catio n where data o bjects like integers and strings are sto red. We
also learned that when yo u run a pro gram, the interpreter creates a namespace. Within namespace, values in object
space are bo und to names by assignment statements, functio n definitio ns, and such.
A Pytho n pro gram has a "glo bal" namespace, where names are bo und by assignments and functio n definitio ns within
the main bo dy o f the pro gram. When yo u call a functio n, Pytho n dynamically creates a new namespace and binds the
argument values to the parameter names. Assignments made during executio n o f the functio n call (no rmally) result in
bindings in the functio n call ("lo cal") namespace. When the functio n returns, the namespace is auto matically
destro yed, and any bindings inside the namespace are lo st. On o ccasio n, this means that so me o f the values will no
lo nger have references. When that happens, the memo ry used to sto re tho se values beco mes reclaimable as
garbage. (Do n't wo rry if yo u do n't have a grip o n all o f this stuff just yet. It'll make mo re sense when we get to the
experimentatio n!)
When we write large pro grams "mo no lithically" (as who le chunks), we may inadvertently use the same name fo r two
different purpo ses at different places in the pro gram. We can avo id that pro blem by inco rpo rating the principle o f
mo dularity into o ur wo rk; we'll write pro grams as co llectio ns o f small chunks that are relatively independent o f o ne
ano ther. This will also make o ur pro grams easier to read and understand.
With Pytho n, we are able to co nstruct many independent namespaces and handle them separately. The same name
can be defined in two different namespaces, because the uses do n't co llide. When the interpreter lo o ks fo r the value
bo und to a particular name, it lo o ks in three specific namespaces. First, it lo o ks in the lo cal namespace (assuming a
functio n call is active). Next, it lo o ks in the glo bal namespace. Finally, it lo o ks in the "built-in" namespace, which ho lds
the names o f o bjects that are hard-wired into the Pytho n interpreter, like exceptio ns and built-in functio ns.
Python Modules
A mo dule is a co llectio n o f statements that are executed. Every pro gram yo u have written so far in this co urse is a
Pytho n mo dule. Yo u wro te them as stand-alo ne pro grams. When yo u run a mo dule as a pro gram, the interpreter
terminates after all o f the co de has been executed. Running the pro gram is o ne way to cause its co de to be executed.
Ano ther way is to import it. When yo u write im po rt m o dx in yo ur pro gram, the interpreter lo o ks fo r the m o dx.py file. It
also lo o ks fo r its co mpiled versio n: m o dx.pyc. If m o dx.pyc is up to date, it will save the interpreter the wo rk o f
co mpiling it.
If the file is no t fo und, an ImportError exceptio n is raised. Otherwise, the interpreter executes the co de in the mo dule,
and binds the mo dule's namespace to the name o f the mo dule in the current namespace. So , if m o dx defines a
functio n f (), after yo u have impo rted the mo dule, yo u can call that functio n with m o dx.f ()—the do t o perato r tells the
interpreter to lo o k up the name f in the namespace bo und to the name m o dx.
Suppo se mo dule z defines functio n g(), mo dule y impo rts mo dule z, and yo ur pro gram impo rts mo dule y. Yo u co uld
call the functio n as y.z.g(). The interpreter wo uld lo o k up y in the lo cal namespace, retrieving the namespace o f
mo dule y. Then it wo uld lo o k up z in that namespace, retrieve the namespace o f mo dule z, and in that namespace
lo o k up the name g and retrieve the functio n.
Okay, I think we've go t eno ugh to think abo ut. Let's get busy with so me practical applicatio n! We'll create a pro gram
called im po rt e r.py that impo rts a mo dule called m o da, that in turn impo rts a mo dule called m o db. The pro gram is
go ing to call a functio n defined in m o db. Create the m o da.py, m o db.py, and im po rt e r.py pro grams, respectively,
as sho wn, in yo ur /pyt ho n1 fo lder:
CODE TO TYPE:
import modb
CODE TO TYPE:
def triple(x):
"""Simple function returns its argument times 3."""
return x*3
CODE TO TYPE:
#!/usr/local/bin/python3
"""importer.py: imports moda and calls a function from a module moda imports."""
import moda
print(moda.modb.triple("Yippee! "))
Save them all, and run the im po rt e r.py pro gram. When it runs, it impo rts mo dule mo da. This binds the mo da
mo dule's namespace to the name m o da in the pro gram's (glo bal) namespace. When mo dule mo da is impo rted, its
co de is executed. This causes mo dule mo db to be impo rted, binding it to the name m o db o n mo dule mo da's
namespace. When mo db is impo rted by mo da, its co de is executed, and the de f statement binds the name t riple to
the functio n definitio n in mo db's namespace.
No w when the interpreter sees the statement print (m o da.m o db.t riple (" Yippe e ! " )), it lo o ks up the name m o da in
the glo bal namespace, then lo o ks up the name m o db in that namespace, and finally lo o ks up the name t riple in that
namespace. This final lo o kup returns a reference to the triple functio n, which is then called with the argument "Yippee! "
and yo ur pro gram will print "Yippee! Yippee! Yippee! ".
The namespace labeled "GLOBAL NAMESPACE" is actually the glo bal namespace o f the im po rt e r mo dule run as
the main pro gram. This diagram sho ws the relatio nship between the namespaces o f the vario us mo dules:
When a mo dule is impo rted by a pro gram, the interpreter binds the special name __nam e __ in the mo dule's
namespace to its name. When a mo dule is run as a pro gram, __nam e __ receives a special value "__main__". Yo u
can assume that yo ur mo dule will be impo rted, but if it gets run as a pro gram (that is, if __name__ == "__main__"),
then the user isn't trying to use it, but instead wants to test it.
So me standard library mo dules have a sectio n at the end that co ntains the statement:
The co de that fo llo ws that statement is there to test the mo dule's functio nality.
We'll write co de like that to test o ur functio ns as well, and make it easier to verify that they wo rk as intended. The mo re
yo u do to make yo ur mo dules self-testing, the easier it is to detect when a small change has bro ken the co de.
Fo rtunately, Pytho n lets yo u write yo ur co de as a co llectio n o f modules, each o f which is a separate text file. This
makes it easier to use yo ur co de in vario us co ntexts.
Let's take a pro gram that uses functio ns and split it into two pieces. Create this pro gram in the edito r windo w:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Contains functions to manipulate number representations."""
def commafy(val):
if len(val) < 4:
return val
out = []
while val:
out.append(val[-3:])
val = val[:-3]
return ",".join(reversed(out))
def commareal(val):
if "." in val:
before, after = val.split(".", 1)
else:
before, after = val, "0"
return "{0}.{1}".format(commafy(before), after)
The first mo dule defines the required functio ns. The seco nd pro duces results by calling o ne o f the functio ns. It gains
access to the functio n it needs by impo rting the mo dule that defines it.
The co m m af y functio n takes a who le number (which is assumed to be a string co mprising all digits) and, beginning
fro m the right, splits it into chunks o f three digits. The value string is sho rtened to remo ve each chunk after it is added to
the o ut list. Any chunk o f less than three digits that remains at the end will be captured auto matically by slicing. When
no digits remain, the o ut list is reversed to put the chunks in the co rrect o rder, and the chunks are jo ined to gether with
co mmas to pro vide the functio n's return value.
The co m m are al() functio n takes a string representatio n o f a real number o r integer. If the string co ntains a decimal
po int, it is split aro und that. If there is no decimal po int, a single "0 " is used. The co m m af y() functio n is used to insert
co mmas into the po rtio n befo re the decimal po int, and the o utput string is co nstructed fro m the "co mmafied" po rtio n
befo re the decimal po int and the unchanged po rtio n after the decimal po int.
Altho ugh this mo dule is designed to be impo rted by o ther pro grams, it will test itself if it's run as a main pro gram. It
iterates o ver a set o f integers, printing o ut the number, its "co mmafied" versio n, and the co m m are al() value o f the
number divided by 1,0 0 0 and represented to two decimal places. When the mo dule is impo rted, the co nditio n if
__nam e __ == " __m ain__" is false, so the testing co de do es no t execute.
No w, create this pro gram:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Take user input, convert to float, and print
out the number to two decimal places, with commas."""
import funcs
while True:
inval = input("Enter a number: ")
if not inval:
break
number = float(inval)
print(funcs.commareal("{0:.2f}".format(number)))
Save it in yo ur /pyt ho n1 fo lder as f uncalls.py and run it. This pro gram perfo rms an infinite lo o p, terminated fro m
within when the user presses Ent e r witho ut typing a number in respo nse to the "Enter a number" pro mpt. Otherwise,
the user's input is co nverted to a flo ating-po int number, and is fo rmatted back into a string representatio n with two
decimal places. The result o f the co m m are al() functio n is printed back to the user (via f uncs.py) befo re the lo o p
repeats.
import ... as
What if yo u need to impo rt a mo dule, but yo u've already used its name in yo ur co de? Yo u can avo id rewriting
yo ur co de using the im po rt ... as syntax, which allo ws yo u to impo rt a mo dule using a name o f yo ur cho ice
rather than its natural name. So , if yo u write im po rt t im e as t , the mo dule is impo rted in the standard way,
but rather than being bo und to its standard name in the impo rting namespace, the mo dule namespace is
bo und to the name t . No w yo u can write a call o n the asct im e () functio n in the mo dule as t .asct im e (), and
co ntinue to use the name "time" fo r o ther purpo ses.
An alternative way to handle situatio ns where the name "time" is already in use, is to impo rt the "asctime"
name into the current namespace directly with f ro m t im e im po rt asct im e , and write the calls o n the
functio n as asct im e (). Because the __m ain__ namespace co ntains no direct reference to the t im e mo dule,
o ther names in t im e 's namespace are no t available to the __m ain__ mo dule. The name asct im e is co pied
fro m the t im e mo dule's namespace to the __m ain__ namespace:
Under mo st circumstances, yo u do no t want to use f ro m ... im po rt ... to impo rt all names defined in a
mo dule using the statement f ro m m o dule im po rt *. While this may seem like a great way to define the
necessary symbo ls, it puts the impo rted mo dule in charge o f what gets lo aded into yo ur namespace. Unless
yo u are really familiar with the impo rted mo dule's co de, yo u'll have no way o f kno wing whether it defined
symbo ls that yo u're already using. If it did define them, they will o verwrite yo ur definitio ns o r yo ur symbo ls will
o verwrite the definitio n fro m the mo dules. Either way, yo u'll receive no no tificatio n that this has happened, and
yo u will be left with a tricky debugging exercise.
Certain well-written and so phisticated library mo dules (such as the Tkinter graphical user interface library)
reco mmend this fo rm o f impo rt. Do no t try to emulate this in yo ur o wn designs—it is an invitatio n to disaster!
Let's lo o k at the system path. It is defined, appro priately eno ugh, in a mo dule called sys. Yo u have to impo rt it befo re
yo u can examine it. To see what's o n the path, type the fo llo wing co mmands in an interactive sessio n:
INTERACTIVE SESSION:
/usr/local/python34/lib/python34.zip
/usr/local/python34/lib/python3.4
/usr/local/python34/lib/python3.4/plat-linux
/usr/local/python34/lib/python3.4/lib-dynload
/usr/local/python34/lib/python3.4/site-packages
>>>
When the interpreter lo o ks fo r a mo dule, it searches these paths, starting at the to p o f the list, and sto pping when it
finds the mo dule. This path can be useful to kno w if yo u have a pro gram that do esn't seem to be finding the mo dule
yo u wanted it to find.
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
More About Functions
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
capture keyword arguments who se names do no t co rrespo nd to the name o f any parameter.
specify keywo rd parameters in a functio n that has a dict-parameter.
impo rt functio ns and help().
execute functio ns by dispatch.
No w that yo u've go t the basics o f functio ns do wn, we'll build o n that kno wledge with keywo rd parameters, switches, impo rting
functio ns, and mo re!
CODE TO TYPE:
#!/usr/local/bin/python3
""" Demonstrates capture of keyword arguments"""
def keywords(**kwargs):
"Prints the keys and arguments passed through"
for key in kwargs:
print("{0}: {1} ".format(key, kwargs[key]))
def keywords_as_dict(**kwargs):
"Returns the keyword arguments as a dict"
return kwargs
if __name__ == "__main__":
keywords(guido="Founder of Python", python="Used by NASA and Google")
print(keywords_as_dict(guido="Founder of Python", python="Used by NASA and Google")
)
Save it in yo ur /pyt ho n1 fo lder as ke ywo rd_args.py, and run it. Yo u o utput lo o ks like this:
OBSERVE:
The pro gram has two functio ns that capture general keywo rd arguments. When yo u call such a functio n, the interpreter
matches up the po sitio nal and keywo rd arguments with their co rrespo nding parameters, then takes any unmatched
keywo rd arguments and puts them into a dict, which it binds to the dict-parameter. The first functio n, ke ywo rds(),
iterates o ver the keys o f the dict, printing the keys (which are the names o f the unmatched keywo rd arguments) and the
asso ciated values (which are the values fo llo wing the equals signs). The seco nd functio n, ke ywo rds_as_dict (), just
returns the keywo rd arguments, demo nstrating that the dict-parameter is in fact a dict.
CODE TO TYPE:
#!/usr/local/bin/python3
def description(name, instructor, *students, **staff):
"""Print out a course description.
name: Name of the course
instructor: Name of the instructor
*students, ...: List of student names (positional arguments)
**staff, ...: List of additional staff (keyword arguments)
"""
print("=" * 40)
print("Course Name:", name)
print("Instructor:", instructor)
print("-" * 40)
for title, name in staff.items():
print(title.capitalize(), ": ", name)
print("{0:-^40}".format(" registered students "))
for student in students:
print(student)
if __name__ == "__main__":
description("Python 101",
"Steve Holden",
"Georgie Peorgie",
"Mary Lamb",
"Penny Rice",
publisher="O'Reilly School of Technology",
author="Python Software Foundation"
)
description("Django 101",
"Jacob Kaplan-Moss",
"Baa-Baa Blacksheep",
"Mary Contrary",
"Missy Muffet",
"Peter Piper",
publisher="O'Reilly School of Technology",
author="Django Software Foundation",
editor="Daniel Greenfeld"
)
cold1:~$ cd python1
cold1:~/python1$ ./courses.py
========================================
Course Name: Python 101
Instructor: Steve Holden
----------------------------------------
Publisher : O'Reilly School of Technology
Author : Python Software Foundation
--------- registered students ----------
Georgie Peorgie
Mary Lamb
Penny Rice
========================================
Course Name: Django 101
Instructor: Jacob Kaplan-Moss
----------------------------------------
Publisher : O'Reilly School of Technology
Editor : Daniel Greenfeld
Author : Django Software Foundation
--------- registered students ----------
Baa-Baa Blacksheep
Mary Contrary
Missy Muffet
Peter Piper
if __name__ == "__main__":
description("Python 101",
"Steve Holden",
"Georgie Peorgie",
"Mary Lamb",
"Penny Rice",
publisher="O'Reilly School of Technology",
author="Python Software Foundation"
)
description("Django 101",
"Jacob Kaplan-Moss",
"Baa-Baa Blacksheep",
"Mary Contrary",
"Missy Muffet",
"Peter Piper",
publisher="O'Reilly School of Technology",
author="Django Software Foundation",
editor="Daniel Greenfeld"
)
The first and seco nd parameters (nam e and inst ruct o r) are po sitio nal, and so are bo und to the first and seco nd
arguments o f any call. Any additio nal po sitio nal arguments are placed into the st ude nt s tuple. Finally, any keywo rd
arguments are placed into the st af f dict.
The nam e and inst ruct o r parameters are printed o ut. The functio n then iterates o ver the items (each item is a (key,
value) pair o f the st af f dict-parameter) to print details abo ut any additio nal staff. Finally, the functio n lo o ps thro ugh the
st ude nt s to list the individuals taking the class.
Take care when using sequence- and dict-parameters. With regular (po sitio nal and keywo rd)
parameters, yo u can usually determine the interface o f the functio n (that is, ho w it sho uld be called)
fro m the functio n and parameter names. When sequence- and dict-parameters are used, this is
mo re difficult to determine.
WARNING
If yo u do use sequence- and dict-parameters, make sure yo u do cument the purpo se o f each
parameter in the functio n's do cstring. This is go o d practice in any case, but especially so when the
interface is mo re co mplex.
Let's take a clo ser lo o k at what o ur do cstrings give us. Try these co mmands in an interactive sessio n:
INTERACTIVE SESSION:
(END)
By do cumenting yo ur functio n co rrectly, yo u've pro vided useful info rmatio n to anyo ne who impo rts yo ur mo dule. (Yo ur
fello w pro grammers thank yo u!) Of co urse, the mo dule itself can also have useful do cumentatio n, tho ugh in this case,
there just wasn't much to pro vide. Co ntinue yo ur previo us interactive sessio n to verify that yo ur do cumentatio n
appears as expected:
INTERACTIVE SESSION:
>>> help(courses)
Help on module courses:
NAME
courses
FUNCTIONS
description(name, instructor, *students, **staff)
Print out a course description.
name: Name of the course
instructor: Name of the instructor
*students, ...: List of student names (positional arguments)
**staff, ...: List of additional staff (keyword arguments)
FILE
/users/smiller/python1/courses.py
(END)
Nice! The interpreter created a manual page fo r yo ur mo dule, just fro m the do cumentatio n strings that yo u entered.
No w anyo ne who wants to use yo ur mo dule can impo rt it into an interactive sessio n and learn all abo ut it using
Pytho n's standard he lp() functio n. I like it!
NAME
keyword_args - Demonstrates capture of keyword arguments
FUNCTIONS
keywords(**kwargs)
Prints the keys and arguments passed through
keywords_as_dict(**kwargs)
Returns the keyword arguments as a dict
FILE
/users/smiller/python1/keyword_args.py
(END)
So , thanks to the he lp() metho d, we can use the interactive interpreter to find impo rtant info rmatio n abo ut the functio ns
we've written. These co de statements are really driven by the do cstrings yo u write into yo ur Pytho n co de. All o f the
functio ns o f a mo dule are part o f its do cumentatio n.
Sweet. If all o f this isn't eno ugh to make yo u start sprinkling do c strings aro und yo ur co de, then no thing will persuade
yo u! Yo u can do cument mo dules, functio ns, and classes just by making their first executable statement a
do cumentatio n string. That's the kind o f simple po wer that makes Pytho n famo us!
Thankfully, Pytho n gives yo u a go o d way to wo rk aro und this using to o ls yo u've already learned. Yo u can write each
alternative set o f actio ns as a functio n, and then use a dictio nary to define lo gic flo w. The keys represent po ssible
actio ns, and the functio ns are the actio ns themselves. This so unds a lo t mo re co mplex than it actually is; let's use an
example to clarify things:
INTERACTIVE SESSION:
First we created the two simple functio ns, add() and sub(), then we placed them inside the sw dict. Then we called
them (like any o ther Pytho n dict) by referencing their keys, and passed in arguments. This pro vides a nice, clean way o f
o rganizing and calling functio ns. In the last two lines o f the example, we printed o ut the lo gic flo w. When a dict o f
functio ns is used this way, it is called a dispatch table.
Ready fo r a mo re co mplex example? Go o d! We'll put five functio ns into a dict, then use a while lo o p and an input
statement to act as o ur user interface. We'll dispatch the appro priate functio n acco rding to the user's input. A lo t o f this
will lo o k familiar to yo u. Let's go ahead and get it wo rking. Create a new pro gram as sho wn belo w:
CODE TO TYPE:
#!/usr/local/bin/python3
""" A program designed to display switching in Python """
import sys
if __name__ == "__main__":
switch = {
'text': print_text,
'args': print_args,
'kwargs': print_kwargs,
'all': print_all,
'quit': quit
}
options = switch.keys()
prompt = 'Pick an option from the list ({0}): '.format(', '.join(options))
while True:
inp = input(prompt)
option = switch.get(inp, None)
if option:
option('Python','is','fun',course="Python 101",publisher="O'Reilly")
print('-' * 40)
else:
print('Please select a valid option!')
Save it in yo ur /pyt ho n1 fo lder as swit ch.py, and run it. Try all the different o ptio ns. Also , try typing so mething that
isn't o ne o f the o ptio ns. Befo re we start reviewing this pro gram, take a minute and check o ut the difference between this
pro gram and earlier o nes in the co urse. Do esn't this o ne just look cleaner?
import sys
if __name__ == "__main__":
switch = {
'text': print_text,
'args': print_args,
'kwargs': print_kwargs,
'all': print_all,
'quit': quit
}
options = switch.keys()
prompt = 'Pick an option from the list ({0}): '.format(', '.join(options))
while True:
inp = input(prompt)
option = switch.get(inp, None)
if option:
option('Python','is','fun',course="Python 101",publisher="O'Reilly")
print('-' * 40)
else:
print('Please select a valid option!')
All o f the functio ns insist o n the same arguments, even if mo st o f them o nly use a po rtio n o f tho se arguments. The
f irst t hre e f unct io ns are clear eno ugh; the f o urt h f unct io n just calls all three o f them, and the last f unct io n
uses the Pytho n standard library sys mo dule to quit the pro gram.
No w, let's mo ve o n to everything that fo llo ws if __nam e __ == " __m ain__" :. First, we create the swit ch dict , which
has five elements—the values are each o f the previo usly defined functio ns. Then, we co nstruct an o pt io ns list fro m
the swit ch.ke ys()—keys o f the switch dict. Then, we pro mpt the user fo r an o ptio n and start the input lo o p.
In the input lo o p, o pt io n = swit ch.ge t (inp, No ne ) takes the user's o ptio n and either finds the functio n in questio n o r
returns a No ne o bject. If an o ptio n is fo und (if o pt io n), then the parameters are passed to the user-selected functio n.
If no o ptio n is fo und, the user is pro mpted to 'Ple ase se le ct a valid o pt io n!'.
The result is a cleaner applicatio n where reuse o r integratio n o f new functio ns is much easier. Fo r example, let's add in
the de script io n() functio n fro m the co urse s.py mo dule yo u wro te earlier in this lesso n. Mo dify the co de and the
switch dict as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
""" A program designed to display switching in Python """
import sys
import courses
if __name__ == "__main__":
switch = {
'text': print_text,
'args': print_args,
'kwargs': print_kwargs,
'all': print_all,
'course': courses.description,
'quit': quit
}
options = switch.keys()
prompt = 'Pick an option from the list ({0}): '.format(', '.join(options))
while True:
inp = input(prompt)
option = switch.get(inp, None)
if option:
option('Python','is','fun',course="Python 101",publisher="O'Reilly")
print('-' * 40)
else:
print('Please select a valid option!')
Save and run it. Cho o se the co urse o ptio n; yo ur results may seem a little silly, but they are co rrect based o n the
argument being passed to the functio n—and the instructo r does exist, and we think students are fun! See ho w easily we
can integrate new functio nality into o ur pro gram? The lo gic do esn't change at all, o nly the data that drives it.
Keep in mind, as we push o n, that go o d practice fo r Pytho n develo pers means never repeating any stanza o f co de
twice. Instead, put it into a functio n, and call the functio n twice!
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Classes and Object-Oriented Programming
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
create the individual instances that behave acco rding to their (built-in) type definitio n.
define yo ur o wn o bject classes.
define o bject behavio r.
bind a name during the executio n o f the class bo dy.
define yo ur o wn data types called classes.
use __init__().
The Pytho n language co ntains so me built-in data types. The interpreter kno ws ho w o bjects o f a given type sho uld
behave, but o f co urse, it has no idea which instances o f which types yo ur pro grams will create. So , the interpreter
co ntains the definitions o f the data types, but yo ur pro gram creates the individual instances, each o f which behaves
acco rding to its (built-in) type definitio n.
In o ur first example here, we'll explo re the nature o f o ne o f Pytho n's o bjects: the co mplex number. Type the co mmands
as sho wn:
INTERACTIVE SESSION:
>>> c = 3+4j
>>> type(c)
<class 'complex'>
>>> dir(c)
['__abs__', '__add__', '__bool__', '__class__', '__delattr__', '__divmod__', '__doc__',
'__eq__',
'__float__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs_
_', '__gt__',
'__hash__', '__init__', '__int__', '__le__', '__lt__', '__mod__', '__mul__', '__ne__',
'__neg__',
'__new__', '__pos__', '__pow__', '__radd__', '__rdivmod__', '__reduce__', '__reduce_ex_
_',
'__repr__', '__rfloordiv__', '__rmod__', '__rmul__', '__rpow__', '__rsub__', '__rtruedi
v__',
'__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '
conjugate',
'imag', 'real']
>>> c
(3+4j)
>>> c.__add__
<method-wrapper '__add__' of complex object at 0x01A84110>
>>> c.real
3.0
>>> c.imag
4.0
>>> type(c.imag)
<class 'float'>
>>> c.imag = 2.5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: readonly attribute
The interpreter repo rts that the type o f c is <class 'co m ple x'>. The call o n dir(c) sho ws us that many metho ds have
names that begin with do uble undersco res— a lo t o f the names represent o perato rs that yo u may want to use o n a
co mplex number (add, subtract, divide, and so o n). Two o f the names do no t begin with do uble undersco res: "real"
and "imag." In mathematics, co mplex numbers have a real and an imaginary part. Each co mplex number in Pytho n has
two attributes called re al and im ag; tho se names are bo und to flo ating-po int numbers. Yo u can access the value o f
each attribute separately, but because all numbers in Pytho n are immutable, the interpreter wo n't allo w yo u to change
them.
We establish the type with a class statement. Then we create an instance o f o ur new class named f irst , by calling
First () as tho ugh it were a functio n. The interpreter identifies the instance by its name and address in hexadecimal
(base 16 ): "<__main__.First o bject at 0 x0 26 9 9 A9 0 >". This instance o f yo ur class is equipped to behave in certain
ways. The behavio rs sho wn in the result o f the dir(f irst ) call are co mmo n to all Pytho n o bjects.
Unlike built-in instances o f classes, when using classes yo u create yo urself, yo u can bind values to named attributes.
These bindings wo rk just like the binding o f values to keys in a dict, because, in fact, they are dicts. Assignment to a
do tted name results in that name being added as a key to a dict named __dict __, with the asso ciated value beco ming
the dict value. Fo r mo st names, inst .nam e is equivalent to inst .__dict __[" nam e " ].
>>> Second.__dict__.keys()
['__module__', 'two', 'one', '__dict__', '__weakref__', '__doc__']
>>> Second.__dict__["one"]
'Depp'
>>> Second.one
'Depp'
>>> second = Second()
>>> dir(second)
['__class__', '__delattr__', '__dict__', '__doc__', ...,
'__weakref__', 'one', 'two']
>>> second.__dict__
{}
>>> second.two
'Pitt'
>>> Second.two = "Clooney"
>>> second.two
'Clooney'
Do n't clo se that interactive sessio n; we'll be using it again sho rtly. Our example sho ws so me subtle differences
between classes and instances. Altho ugh each co ntains a __dict __, the instance was a dict already, and bindings to
the instance are seen o nly in that dict. In the class, ho wever, the bo und names also appear as part o f the class's
namespace, and __dict __ is no lo nger a dict, but so mething called a dict_pro xy. A dict_pro xy pro vides a selective
view o f the class's namespace. These differences are significant to a Pytho n implementer, but fo r no w yo u can file this
info rmatio n away.
Mo re impo rtantly, no tice that the names o ne and t wo have been bo und in the class namespace, and no w also appear
in the instance namespace (tho ugh no t in its __dict __). In additio n, they have the same value in the instance
namespace as they do in the class namespace. If yo u rebind the name in the class namespace, it also changes in the
instance. Our example demo nstrates that names that appear to be in the instance namespace are actually defined in
the class.
We'll take a clo ser lo o k at the relatio nship between a class and its instances later. Fo r the mo ment, just be aware that
yo u can access attributes o f the class in any o f its instances. If yo u bind the same attribute to the instance, it do es no t
change the class at all—the binding remains lo cal to the instance. Co ntinuing the interactive sessio n, type the
co mmands belo w as sho wn:
INTERACTIVE SESSION:
Here we created a seco nd Se co nd instance, named se co nd2, which initially sho wed the same value as the class fo r
its o ne attribute. When we assigned "Blo o m" to the se co nd2 instance's o ne attribute, it o verrides the class attribute,
but o nly fo r that o ne instance. The se co nd instance's o ne attribute still reflects the class's value fo r that attribute.
When the Se co nd class's o ne attribute is rebo und, the se co nd instance's o ne attribute also changes, but no t that o f
the se co nd2 instance. (Phew! Did yo u catch all that?)
The attributes o f a class can be accessed by all instances o f that class but, as we've just seen, an assignment to an
instance attribute o f the same name will o verride the class attribute. Check o ut the last two expressio ns in the last
sessio n; no t o nly do es the se co nd2 instance have a o ne attribute (the o ne inherited fro m the Se co nd class) in its
namespace, it also has a o ne attribute in its __dict __ as a result o f being bo und to se co nd2.o ne . The interpreter is
lo o king in an instance's __dict __ first, and o nly lo o ks in the namespace if it fails to find the attribute in the dict.
The resulting co de, with a co uple o f calls o n the functio ns to test the co de, might lo o k like the pro gram we'll create
no w. Create a new pro gram as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Initial implementation of complex numbers."""
class Cplx:
pass
def cstr(c):
return "%s+%sj" % (c.real, c.imag)
if __name__ == "__main__":
zero = cplx(0.0, 0.0)
one = cplx(1.0, 0.0)
i = cplx(0.0, 1.0)
result = cadd(zero, cadd(one, i))
print(cstr(result))
Save it in yo ur /pyt ho n1 fo lder as cplx.py, and run it. The result 1.0 +1.0 j prints. Yo u aren't using very much o f
Pytho n's class mechanism tho ugh. To do that, yo u need to separate the creatio n o f the instances fro m their
initializatio n. Then yo u'll rename the cplx() functio n to cinit (), and change its co de so that it o perates o n an existing
rather than a new instance, initialize it and return the instance. This initially co mplicates yo ur calling co de, because yo u
no w have to create the instances befo re initializing them, but do n't wo rry abo ut that no w. Let's play with so me co de!
Mo dify yo ur pro gram as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Initial implementation of complex numbers."""
class Cplx:
pass
def cstr(c):
return "%s+%sj" % (c.real, c.imag)
if __name__ == "__main__":
zero = Cplx()
cinit(zero, 0.0, 0.0)
one = Cplx()
cinit(one, 1.0, 0.0)
i = Cplx()
cinit(i, 0.0, 1.0)
result = cadd(zero, cadd(one, i))
print(cstr(result))
Save and run it. It prints the same result as befo re—after all, it's really the same co de.
Go ahead and edit cplx.py so that the functio ns beco me metho ds o f the class:
Note To indent the functio n declaratio ns, just select the blo ck o f co de yo u want to indent and press T ab.
CODE TO TYPE:
#!/usr/local/bin/python3
"""Initial implementation of complex numbers."""
class Cplx:
pass
def cstr(c):
return "%s+%sj" % (c.real, c.imag)
if __name__ == "__main__":
zero = Cplx()
Cplx.cinit(zero, 0.0, 0.0)
one = Cplx()
Cplx.cinit(one, 1.0, 0.0)
i = Cplx()
Cplx.cinit(i, 0.0, 1.0)
result = Cplx.cadd(zero, Cplx.cadd(one, i))
print(Cplx.cstr(result))
Save and run it. Yo u might see warnings o n the de f lines stating that the metho ds sho uld have self as the first
parameter, but yo u can igno re them fo r no w. Yo u'll still get this result: 1.0 +1.0 j.
By declaring a functio n as part o f the class bo dy, we bind the functio n name within the class namespace rather than the
mo dule namespace. This means that, to call the functio n, it must be preceded by the class name and a do t. Because
the class bo dy is no lo nger empty, yo u do n't need the pass statement any mo re.
No w let's break yo ur co de! Do n't wo rry; we'll fix it right up o nce yo u understand the details o f the breakage. The Cplx
class has three new attributes—cinit , cadd, and cst r. Yo u can access class attributes (attributes bo und in the class
namespace) thro ugh an instance o f the class. So yo u'd think that yo u co uld access tho se metho ds thro ugh the
instance, rather than the class. But when yo u change the co de to do that, a strange erro r o ccurs. Mo dify cplx.py to call
the metho ds o n the instances as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Initial implementation of complex numbers."""
class Cplx:
def cstr(c):
return "%s+%sj" % (c.real, c.imag)
if __name__ == "__main__":
zero = Cplx()
zero.cinit(zero, 0.0, 0.0)
one = Cplx()
one.cinit(one, 1.0, 0.0)
i = Cplx()
i.cinit(i, 0.0, 1.0)
result = zero.cadd(zero, one.cadd(one, i))
print(result.cstr(result))
Save and run it. Yo u might be surprised to see a traceback and erro r message:
OBSERVE:
This message may be a bit difficult to understand. It says that ze ro .cinit (ze ro , 0 .0 , 0 .0 ) has fo ur arguments, but it's
clear that it pro vides o nly three. Where is the so urce o f the fo urth argument?
When the interpreter sees a reference to a class's metho d relative to an instance, it assumes that the metho d will need
to kno w which instance it was being called upo n. Co nsequently, it inserts the instance as the first argument
auto matically. Metho ds are being called with to o many arguments because the interpreter assumes yo u will want a
reference to the instance, and inserts it auto matically. The fix fo r yo ur co de is to remo ve the explicit instance
arguments. Fix cplx.py as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Initial implementation of complex numbers."""
class Cplx:
def cstr(c):
return "%s+%sj" % (c.real, c.imag)
if __name__ == "__main__":
zero = Cplx()
zero.cinit(zero, 0.0, 0.0)
one = Cplx()
one.cinit(one, 1.0, 0.0)
i = Cplx()
i.cinit(i, 0.0, 1.0)
result = zero.cadd(zero, one.cadd(one.cadd(i))
print(result.cstr(result))
Save and run it. Yo u sho uld get 1.0 +1.0 j as yo ur result again.
Using __init__()
When yo u create an instance o f a class by calling it, the interpreter lo o ks to see whether the class has an
__init __() metho d. If it finds __init __(), it calls that metho d o n the newly-created instance. Because it's an
instance metho d call, the new instance is inserted as the first argument to the call. Further, if the call to the
class has any arguments, they are passed to __init __() as additio nal arguments.
The __init __() metho d m ust no t return a value. If __init __() returns so mething, it affects the
Note instance creatio n pro cess. This causes the interpreter to raise an exceptio n, and yo ur pro gram
to fail. Yo u'll learn abo ut instance creatio n in mo re detail later.
By renaming the Cplx class's cinit () metho d to __init __(), yo u can sho rten the co de that creates and
initializes the new instance to a single line. Very nice. Pytho n users appreciate elegance and simplicity. Ugly
Pytho n co de may be a sign that the language isn't being used to its full advantage. Let's try a bit mo re
experimentatio n. Edit cplx.py belo w as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Initial implementation of complex numbers."""
class Cplx:
def cstr(c):
return "%s+%sj" % (c.real, c.imag)
if __name__ == "__main__":
zero = Cplx(0.0, 0.0)
one = Cplx(1.0, 0.0)
i = Cplx(0.0, 1.0)
result = zero.cadd(one.cadd(i))
print(result.cstr())
Save and run it. Yo u'll get 1.0 +1.0 j fo r a result yet again. Pytho n o bjects tend to have a lo t o f tho se special
metho ds with names that begin and end with do uble undersco res. To make discussing them easier,
"__init__()" is o ften pro no unced "dunder-init"; "dunder" being an abbreviatio n fo r "do uble under." We'll
co nvert the o ther metho ds o f yo ur co mplex class to "dunder" metho ds in a bit.
Similarly, when yo u write a + b in Pytho n, the interpreter tries to execute the task in a number o f ways: first it
tries to co mpute a.__add__(b) (which requires that a has a __add__ metho d). If that do esn't wo rk, Pytho n
tries to co mpute b.__radd__(a). So , to enable yo ur pro gram to add Cplx o bjects, rename the cadd metho d
to __add__.
Being Selfish
Let's take ano ther quick peek at the first argument o f yo ur class's metho ds—the o ne that the interpreter puts in
auto matically when yo u call a metho d o n an instance. Experienced Pytho n pro grammers wo uld be able to
interpret the co de in the last listing, but they wo uld want to kno w why the argument was called c o r c1.
There is an almo st universal co nventio n that the first argument o f a metho d sho uld be called se lf . Reading
o ther peo ple's pro grams is difficult eno ugh, so it's impo rtant to stick to co nventio n—no t o nly will it make yo ur
co de easier fo r o ther pro grammers to read, it will make it easier fo r you to read as well, and that's an
impo rtant time saver.
So ho w sho uld the co de lo o k when yo u make all the changes discussed in the last two sectio ns? Edit
cplx.py belo w as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Initial implementation of complex numbers."""
class Cplx:
def __str__(self):
return "%s+%sj" % (self.real, self.imag)
if __name__ == "__main__":
zero = Cplx(0.0, 0.0)
one = Cplx(1.0, 0.0)
i = Cplx(0.0, 1.0)
result = zero + one + i
print(result)
Save and run it. Yo u'll still get a result o f 1.0 +1.0 j.
A Solid Foundation
Ho w do es it feel to be an up-and-co ming Pytho n pro grammer? Yo u've really co me a lo ng way! Yo u've
learned the basics o f o bject-o riented pro gramming in Pytho n. The Pytho n interpreter o ffers a lo t o f ho o ks in
the fo rm o f __xxx__() metho ds that yo u can use to make yo ur o wn classes as co nvenient and natural to
wo rk with as the built-in Pytho n types.
In future lesso ns, yo u'll do lo ts mo re o bject-o riented pro gramming; I'm co nfident yo u can handle it!
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Exception Handling
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
Syntax erro rs can and will crash yo ur pro gram, but so o n yo u'll kno w exactly ho w to diagno se and fix them! Let's go
right to wo rk. Type the co mmands belo w in an interactive sessio n as sho wn:
INTERACTIVE SESSION:
To handle this so rt o f erro r message, co rrect the syntax o f yo ur co de so it makes sense to the interpreter. This is the
mo st co mmo n kind o f bug, and no w yo u kno w ho w to squash it!
But even if yo ur co de is syntactically co rrect, it can still thro w exceptio ns when yo u run it. The mo st co mmo n
exceptio ns are TypeErro r, KeyErro r, and NameErro r. The o dds are pretty go o d that yo u've enco untered them already
during this co urse, fo r instance if yo u mistyped a variable name o r entered a no n-numeric value that yo ur pro gram tried
to co nvert into a number. Let's take a lo o k at so mething like that. Type the co mmands belo w as sho wn:
INTERACTIVE SESSION:
Keep this interactive sessio n o pen because we'll be do ing ano ther co de example with the snake s dict.
The first lesso n o f exceptio n handling is learning to catch exceptio ns, and then handle them so that they do n't bring
yo ur pro gram crashing do wn. The next level o f exceptio n handling teaches yo u ho w to handle different types o f
exceptio ns at the same time.
INTERACTIVE SESSION:
>>> try:
... snakes['cobra']
... except KeyError:
... print('Exception detected')
...
Exception detected
The t ry statement attempts to execute the co de co ntained in its indented suite. That suite may be made up o f
several lines o f co de, but this example attempts to evaluate o nly the expressio n snake s['co bra']. (This key
was cho sen intentio nally because it will raise an exceptio n. We kno w yo u can handle it!). This causes the
interpreter to trigger the exceptio n handler fo r the KeyErro r exceptio n, the e xce pt statement. The except suite
co ntains the expressio n print ('Exce pt io n de t e ct e d').
Co ngratulatio ns—yo u caught an exceptio n! Of co urse, the exceptio n handler do es no thing fo r yo u if yo u do n't
handle the co rrect exceptio n. The next example illustrates this po int. Type the co de belo w as sho wn:
INTERACTIVE SESSION:
>>> try:
... 3/0
... except KeyError:
... print("Exception detected")
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero
>>>
Altho ugh the t ry statement has an exceptio n handler, it do esn't handle the actual exceptio n
(Zero Divisio nErro r) that is raised. In this case, the interpreter behaves as if there is no handler. In the
interactive interpreter, this means yo u see a "stack traceback." If an unhandled exceptio n happens when yo u
are running a pro gram, yo u still get the stack traceback, and then the pro gram terminates.
INTERACTIVE SESSION:
Exceptio n handling co mbined with a lo o p is really handy. Yo u write an infinite lo o p, and break o ut o f it when
the user's input do es no t raise an exceptio n. Let's try that pro blem again. Type the co mmands as sho wn:
INTERACTIVE SESSION:
This lo o p wo n't blo w up if a user enters no n-numeric data fo r a numeric field, which is a reaso nably co mmo n
pattern in Pytho n co ding.
CODE TO TYPE:
#!/usr/local/bin/python3
""" Nested exception handling"""
if __name__ == "__main__":
print(divide(1, "string"))
print(divide(2, 0))
print(divide(123, 4))
cold1:~$ cd python1
cold1:~/python1$ ./nested.py
====================
a: 1 / b: string
Invalid types for division
None
====================
a: 2 / b: 0
Divide by zero
None
====================
a: 123 / b: 4
30.75
The statement print (divide (1, " st ring" )) raises a TypeErro r exceptio n because it isn't po ssible to divide a
number by a string. This exceptio n is caught by the inner handler and handled. The functio n then ends witho ut
returning a value, so its result is No ne . The statement print (divide (2, 0 )) also raises an exceptio n, but in
this case it isn't caught by the e xce pt o f the inner t ry because it isn't a TypeErro r. Co nsequently, the
exceptio n "bubbles up" to the next level, where there is a handler fo r the Zero Divisio nErro r that o ccurs. Finally,
the statement print (divide (123, 4 )) represents a legal arithmetic o peratio n and gets past bo th erro r
handlers and returns the appro priate result.
By nesting exceptio n handlers, yo u can catch erro rs that are thro wn at different levels and handle them
appro priately. Every additio nal level o f nesting remo ves so me readability fro m yo ur pro gram, tho ugh, so
avo id do ing it when yo u can. Fo rtunately, yo u can avo id so me o f that because Pytho n allo ws yo u to attach
several e xce pt clauses to a single t ry statement. Edit ne st e d.py belo w as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
""" Nested exception handling"""
if __name__ == "__main__":
print(divide(1, "string"))
print(divide(2, 0))
print(divide(123, 4))
Save and run it. When the exceptio n is raised inside o f the t ry suite, the interpreter tries to match it against
each o f the e xce pt clauses, in turn. If it finds a matching clause, it executes the asso ciated handler suite. If
no ne o f the e xce pt clauses match the exceptio n, then no ne o f the handlers are run, and the interpreter starts
to examine the handlers o f any o uter t ry statements. The o utput fro m running this pro gram sho uld lo o k like
this:
INTERACTIVE SESSION:
cold1:~/python1$ ./nested.py
====================
a: 1 / b: string
Invalid types for division
None
====================
a: 2 / b: 0
Divide by zero
None
====================
a: 123 / b: 4
Sometimes executed
30.75
The print (" So m e t im e s e xe cut e d" ) statement and the fo llo wing re t urn aren't executed when an
exceptio n is raised. One particularly useful feature o f exceptio ns is that yo u can use them to change the flo w
o f yo ur pro gram's lo gic when co nditio ns are, well, exceptio nal.
Raising Exceptions
Yo u may want to be able to flag erro r co nditio ns fro m yo ur o wn co de. This is especially useful when yo u are
writing co de to be used by o ther peo ple. Yo u flag erro r co nditio ns with the raise statement; this is useful in
two co ntexts:
CODE TO TYPE:
#!/usr/local/bin/python3
""" Nested exception handling"""
if __name__ == "__main__":
for arg1, arg2 in ((1, "string"), (2, 0), (123, 4)):
try:
print(divide(arg1, arg2))
except Exception as msg:
print("Problem: {0}".format(msg))
====================
a: 1 / b: string
Something went wrong!
Problem: unsupported operand type(s) for /: 'int' and 'str'
====================
a: 2 / b: 0
Something went wrong!
Problem: int division or modulo by zero
====================
a: 123 / b: 4
30.75
The e xce pt statement in the divide() functio n no w specifies the same handler fo r bo th Zero Divisio nErro r and
TypeErro r exceptio ns. The handler prints an info rmative message ("So mething went wro ng!") and then re-
raises the same exceptio n. Since there are no further handlers in the functio n, the re-raised exceptio n is no w
caught by the e xce pt statement in the main pro gram.
In this case, the e xce pt statement catches pretty much any exceptio n, because all exceptio ns are direct o r
indirect subclasses o f Exce pt io n. Also , the exceptio n specificatio n can be fo llo wed by an as clause, which
specifies a name to bind to the exceptio n that is being handled. Yo u can see fro m the print () functio n call that
when an exceptio n is co nverted to a string, yo u get the message asso ciated with the exceptio n.
CODE TO TYPE:
#!/usr/local/bin/python3
""" Named and generic exception handling"""
class Test(object):
""" Just a simple test class """
if __name__ == "__main__":
s = set((1,2,3))
add(s, 4)
add(1, 4)
add(s, [4, 5, 6])
t = Test()
add(t, 1)
Save it in yo ur /pyt ho n1 fo lder as e xce pt io ns.py, and run it. In o ur add() functio n, we plan fo r 'a' to thro w
either an AttributeErro r, TypeErro r, o r so mething we can't predict. Remember, the plain e xce pt clause must
fo llo w the named exceptio ns. In the last two lines, we attempt to use the add() metho d o f the T e st instance
to use the supplied parameter as an index to a dict with o nly o ne key. Co nsequently, the final call to add()
raises a KeyErro r exceptio n, which in turn causes the final e xce pt clause to be activated, because the
exceptio n raised is neither an AttributeErro r no r a TypeErro r.
After yo u run the pro gram, fo llo w the lo gic thro ugh to make sure yo u understand exactly why it behaves the
way it do es. If there's anything yo u do n't understand abo ut all o f this, talk it o ver with yo ur instructo r.
INTERACTIVE SESSION:
>>> d = {1:'python'}
>>> d[1]
'python'
>>> try:
... d[10]
... except KeyError:
... print('no snake here')
...
no snake here
>>> d.get(10,'no snake here')
'no snake here'
Of co urse, dict .ge t () o nly wo rks if yo u kno w that d is o f type dict. If yo u do n't kno w that, yo u might want to
handle specific exceptio ns raised under tho se circumstances. Fo r example, yo u might try this:
INTERACTIVE SESSION:
>>> d = [1,2,3,4]
>>> try:
... d[10]
... except KeyError:
... print('no snake here')
... except IndexError:
... print('no snake here either')
...
no snake here either
Ideally, yo u build pro grams that never terminate with an uncaught exceptio n. With yo ur new kno wledge o f
exceptio n handling, yo u are much clo ser to reaching that go al.
Yo u're almo st there, just o ne lesso n to go befo re yo ur final pro ject! Great wo rk so far, keep it up!
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.
Building and Debugging Whole Programs
Lesson Objectives
When yo u co mplete this lesso n, yo u will be able to :
Fo r o ur last lesso n to gether in this co urse, we'll gather a bit mo re info rmatio n abo ut ho w to put pro grams to gether.
Yo u'll want to kno w abo ut testing and debugging (lo cating and fixing pro blems in yo ur co de). These to pics are kind o f
like icebergs: much o f their substance lies under the surface and will no t reveal itself immediately; yo u'll co ntinue
learning fo r as lo ng as yo u wo rk with Pytho n. In fact, I have been a practicing pro grammer myself fo r o ver fo rty years,
and I co ntinue to learn mo re and mo re abo ut testing and debugging.
As yo ur grasp o f the language increases, yo u'll inevitably find that when yo u review co de yo u wro te so me
time ago , yo u'll have fo und better ways to express the same algo rithms. It's go o d practice to reevaluate yo ur
co de o ccasio nally—even if it wo rks, it can pro bably be impro ved. On the flipside, as they say, "if it ain't bro ke,
do n't fix it." Unless there's a benefit to changing the co de (like increased speed o r reduced co mplexity), then
leave it alo ne.
It's all to o easy to intro duce subtle erro rs into yo ur pro grams by changing co de to o ther co de yo u think is
equivalent. Until yo u're a bit mo re experienced and co nfident in yo ur decisio ns, just be satisfied with
pro grams that wo rk.
Design T echniques
Two co mmo n terms flung abo ut by pro grammers are top-down and bottom-up design. In to p-do wn design,
yo u defer thinking abo ut the detail o f a pro blem until yo u have mapped o ut the o verall structure it will have.
Wo rking bo tto m-up, yo u begin by building a set o f primitive o peratio ns that yo u can then fo ld to gether with
glue logic to so lve yo ur pro blem.
The to p-do wn appro ach lets yo u avo id having to o much co nfusing detail to deal with early in the design cycle.
Go o d to p-do wn design fo cuses first o n the pro gram's large-scale architectural features.
The bo tto m-up appro ach is useful when yo u already understand yo ur data and the ways yo u need to
manipulate it. Using a t e st -drive n de ve lo pm e nt appro ach to pro gramming, yo u write tests first, and then
write yo ur pro gram to pass the tests. Each functio n and metho d is written to pass its tests, so yo u kno w that
yo ur lo wer-level co mpo nents do indeed behave as expected.
The to p-do wn and the bo tto m-up appro aches can also be used to gether o n the same pro ject. It's a little like
two teams bo ring a tunnel fro m o ppo site sides o f a mo untain: if the two do no t meet, they have no t been
wo rking harmo nio usly to gether.
By taking a to p-do wn appro ach initially, yo u can o perate a divide-and-co nquer scheme, and avo id being
o verwhelmed by detail early in the design. If yo ur co ding pro blem isn't to o co mplex, yo u might find that yo u
have already so lved it befo re yo u ever start wo rking bo tto m-up.
Agile Programming
Agile pro gramming techniques fo cus o n delivering the simplest co de that meets the requirements, o r as agile
practitio ners o ften say, "the simplest thing that co uld po ssibly wo rk." Agile metho ds place great emphasis o n
refactoring yo ur co de when it beco mes to o co mplex. Refacto ring means changing the way yo ur pro gram is
o rganized witho ut changing its behavio r. Refacto ring is generally used when handling large pro grams, but it
can be helpful whenever co mplexity starts to o verwhelm yo u. Refacto ring can help yo u to :
Re m o ve duplicat e co de : When two different functio ns pro vide the same result, o r o ne functio n
is a special case o f ano ther, we refacto r the two functio ns into o ne, and we'll have less co de to
maintain.
Iso lat e e xist ing lo gic f ro m a ne e de d change : If yo u have to change certain cases currently
handled by a single class, yo u might find it advantageo us to refacto r the class by turning it into two
subclasses o f a co mmo n base class. The changed behavio r can then be implemented in just o ne
o f the subclasses.
Make t he pro gram run f ast e r: When perfo rmance beco mes sluggish, it may be that yo ur
o riginal cho ice o f algo rithm o r data structure was inappro priate, so yo u refacto r to streamline yo ur
pro cess.
So me aspects o f agile develo pment are meant to be used by teams o f so ftware develo pers rather than
individuals. Let's go o ver a few key principles that apply to mo st agile techno lo gies:
De sign and co de are t e st -drive n: Whenever yo u add functio nality to yo ur pro gram, yo u first
write a test, fo r auto matic executio n, that checks to make sure that the functio nality is present and
perfo rms pro perly. Yo ur wo rk sho uld pro ceed in small increments—never add two features at the
same time.
Int e grat e co nt inuo usly: Each time yo u change o r fix a mo dule, after running its tests, integrate
the mo dule back into the system and run the system tests to make sure that yo ur change has no t
had any unintended co nsequences.
Re f act o r m e rcile ssly: To re f act o r m e rcile ssly means that if tasks are perfo rmed similarly in
two places, mo ve them aro und so they're do ne in o ne place instead, and then called o r inherited by
the two o riginal places. If yo u have co ding standards and they are vio lated, fix them. If yo u no tice
structural defects, fix them. After each change, rerun all o f yo ur tests to verify that yo ur co de has no t
been bro ken during the refacto ring pro cess.
Re le ase e arly and o f t e n: Release yo ur pro gram to the users befo re adding to o many features.
Yo u can use their feedback to guide further develo pment, and deliver the mo st impo rtant functio ns
o f yo ur pro gram faster.
Ke e p it sim ple : Do n't include co mplexity that yo u think might be handy later. Simplicity has many
benefits, and o ften "later" never arrives.
Co de is no t owned: Agile pro gramming is a team effo rt, so it is never "Jo e's co de" o r "Jim's
co de;" it's "o ur co de." Never fear changing co de created by so meo ne else—it's yo urs to use and
testing will help yo u make sure yo u do n't break it.
Because the do cstrings are available to the pro gram, testing framewo rk can use info rmatio n embedded in
them to verify that co de is functio ning co rrectly.
#!/usr/local/bin/python3
"""Demonstrates the doctest module in action."""
def square(x):
'''Returns the square of a numeric argument.
>>> square(3)
9
>>> square(1000)
1000000
>>> square("x")
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
'''
return x*2
def _test():
import doctest, testable
return doctest.testmod(testable)
if __name__ == "__main__":
_test()
Save it in yo ur /pyt ho n1 fo lder as t e st able .py, and run it. This pro gram co ntains a bug: instead o f
returning its argument raised to the seco nd po wer (squared), the square d() functio n returns its argument
multiplied by two . This is an easy mistake to make—we o nly left o ut a single asterisk—but it renders the
functio n inco rrect. Our o utput lo o ks like this:
OBSERVE:
**********************************************************************
File "/users/smiller/python1/testable.py", line 7, in testable.square
Failed example:
square(3)
Expected:
9
Got:
6
**********************************************************************
File "/users/smiller/python1/testable.py", line 9, in testable.square
Failed example:
square(1000)
Expected:
1000000
Got:
2000
**********************************************************************
File "/users/smiller/python1/testable.py", line 11, in testable.square
Failed example:
square("x")
Expected:
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
Got:
'xx'
**********************************************************************
1 items had failures:
3 of 3 in testable.square
***Test Failed*** 3 failures.
When yo u run the pro gram, it calls the _t e st () functio n, which in turn impo rts the do ct e st mo dule. It also
impo rts the pro gram itself, and then finally calls the do ct e st .t e st m o d() functio n with the mo dule as an
argument. This causes the examples in the square () functio n's do cstring to be run, and co mpared with the
o utput listed under each expressio n.
Because the results do n't agree with the predictio ns in the do cstring, the differences are repo rted as erro rs,
and the o utput makes it clear that so mething is wro ng with the pro gram.
Let's fix the erro r by changing the o peratio n in the square () functio n to an exponentiation (feel free to to ss the
wo rd exponentiation into co nversatio n as well, to impress yo ur friends), as sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Demonstrates the doctest module in action."""
def square(x):
'''Returns the effective length of a string
allowing for tabs of a given length tlen.
>>> square(3)
9
>>> square(1000)
1000000
>>> square("x")
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
'''
return x**2
def _test():
import doctest, testable
return doctest.testmod(testable)
if __name__ == "__main__":
_test()
Save and run it. Yo u get no o utput. That's go o d. The do ctest system is designed to help yo u to detect when
yo ur co de is wo rking inco rrectly and to ho ne in o n the tests that are failing. Yo u'll learn mo re abo ut testing in
o ther co urses, but fo r no w, do ctest is a great place to start. Yo ur do ctests can be integrated into o ther
schemes as yo u mo ve fo rward.
Refactoring
The co ncept o f refacto ring co de can be co mpared to the editio ns o f a textbo o k o ver time. The first editio n
pro vides the main bo dy o f text, while in fo llo wing editio ns, edito rs clean up mistakes, make style changes, o r
add mo re info rmatio n. The co re co ntent o f the bo o k do esn't really change, but the details get better.
When yo u refacto r, yo u aren't adding new functio nality, yo u are making the co de better. Yo u exchange
duplicate co de fo r calls and inheritance where po ssible, fix structural defects, change co de to match co ding
standards (if yo u have them), and mo st impo rtantly, make sure that it passes all of your tests.
If yo u are go ing to refacto r yo ur co de mercilessly, you must have tests. Witho ut sufficient testing, yo u canno t
be certain that yo ur changes have no t bro ken yo ur pro gram.
Let's take so me co de and refacto r it mercilessly. It isn't o ften we strive to be merciless, so let's enjo y this rare
o ppo rtunity! In o ur sample pro gram, we have so me co de that is truly miserable to lo o k at, but it wo rks. Create
the file sho wn belo w:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Demonstrates an opportunity for refactoring."""
>>> list_multiply([3,4],[3,4])
49
>>> list_multiply([1,2,3,4],[10,20])
300
"""
TOTAL_A = 0
for i in LIST_A:
TOTAL_A += i
TOTAL_B = 0
counter = 0
while True:
if counter > len(LIST_B) - 1:
break
TOTAL_B = TOTAL_B + LIST_B[counter]
counter += 1
return TOTAL_A * TOTAL_B
def _test():
import doctest, refactor
return doctest.testmod(refactor)
if __name__ == "__main__":
_test()
Save it in yo ur /pyt ho n1 fo lder as re f act o r.py and run it. Yo u sho uld get no erro rs, but if this co de makes
yo u wince, then yo u are o n track to beco me a go o d Pytho n pro grammer! While the co de is technically co rrect,
it just plain smells. So me variables are upper-case and so me are lo wer-case. Two different lo o ps are used
to do the same actio n o f summing up the integers in two lists, when a simple built-in sum () functio n wo uld
suffice. Can yo u imagine making the necessary alteratio ns if yo u had to add the capability to handle a third o r
fo urth list to yo ur co de? Ouch.
Fo rtunately the co de co mes with do ctests, so yo u can do so me merciless refacto ring. Edit the pro gram as
sho wn:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Refactored version of previous example."""
>>> list_multiply([3,4],[3,4])
49
>>> list_multiply([1,2,3,4],[10,20])
300
"""
def _test():
import doctest, refactor
return doctest.testmod(refactor)
if __name__ == "__main__":
_test()
Huge difference! Save and run it again; the do ctest sho uld still wo rk. Refacto ring like this allo ws yo u to make
changes to impro ve yo ur co de witho ut the fear o f breaking it.
And if yo u need to add functio nality, refacto red co de makes it that much easier. Because the co de is generally
simpler (always remember KISS), it will be less difficult to extend it to wo rk with any number o f lists o f integers.
Try this versio n o f the co de, and make sure it passes the tests:
CODE TO TYPE:
#!/usr/local/bin/python3
"""Adding functionality, much easier with refactored code!"""
def list_multiply(*lists):
""" Sums any number of lists of integers and multiplies them together
>>> list_multiply([3,4],[3,4])
49
>>> list_multiply([1,2,3,4],[10,20])
300
>>> list_multiply([4,3,2,1],[50,50],[5,5,5])
15000
"""
total = 1
for l in lists:
total *= sum(l)
return total
def _test():
import doctest, refactor
return doctest.testmod(refactor)
if __name__ == "__main__":
_test()
Yo u still do n't kno w all there is to kno w abo ut Pytho n (who do es?), but no w yo u're in po sitio n to understand
much o f the Pytho n co de yo u enco unter. Read lo ts o f Pytho n co de; it's a great way to learn mo re abo ut the
language and to increase yo ur understanding o f the library and third-party mo dules it uses. Yo u can practice
do ing just that and applying the Pytho n to o ls yo u've acquired here, in yo ur final pro ject. Thanks so much fo r
taking this Pytho n jo urney with us, it's been a real pleasure wo rking o n it with yo u. Go o d luck!
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.