0% found this document useful (0 votes)
8 views30 pages

Python UNIT II

The document explains the concept of functions in Python, detailing their syntax, purpose, and structure, including the importance of parameters and docstrings for documentation. It illustrates how to define and call functions, emphasizing the flow of execution and the distinction between fruitful and void functions. Additionally, it highlights the ability of functions to call other functions, thereby promoting code reuse and abstraction.

Uploaded by

manohar
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views30 pages

Python UNIT II

The document explains the concept of functions in Python, detailing their syntax, purpose, and structure, including the importance of parameters and docstrings for documentation. It illustrates how to define and call functions, emphasizing the flow of execution and the distinction between fruitful and void functions. Additionally, it highlights the ability of functions to call other functions, thereby promoting code reuse and abstraction.

Uploaded by

manohar
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Functions

In Python, a function is a named sequence of statements that belong together. Their primary purpose is to help us
organize programs into chunks that match how we think about the problem.
The syntax for a function definition is:

def <NAME>( <PARAMETERS> ):


<STATEMENTS>

We can make up any names we want for the functions we create, except that we can’t use a name that is a Python
keyword, and the names must follow the rules for legal identifiers.
There can be any number of statements inside the function, but they have to be indented from the def. In the
examples in this book, we will use the standard indentation of four spaces. Function definitions are the second of
several compound statements we will see, all of which have the same pattern:
1. A header line which begins with a keyword and ends with a colon.
2. A body consisting of one or more Python statements, each indented the same amount — the Python style
guide recommends 4 spaces — from the header line.
We’ve already seen the for loop which follows this pattern.
So looking again at the function definition, the keyword in the header is def, which is followed by the name of the
function and some parameters enclosed in parentheses. The parameter list may be empty, or it may contain any
number of parameters separated from one another by commas. In either case, the parentheses are required. The
parameters specifies what information, if any, we have to provide in order to use the new function.
Suppose we’re working with turtles, and a common operation we need is to draw squares. “Draw a square” is an
abstraction, or a mental chunk, of a number of smaller steps. So let’s write a function to capture the pattern of this
“building block”:

63
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

1 import turtle
2

3 def draw_square(animal, size):


4 """
5 Make animal draw a square with sides of length size. """
6 for _ in range(4): [Link](size) [Link](90)
7

10

11

12 window = [Link]() [Link]("lightgreen")


# Set up the window and its attributes
13

14 [Link]("Alex meets a function")


15

16 alex = [Link]() draw_square(alex,


# Create alex 50) [Link]()
17 # Call the function to draw the square
18

This function is named draw_square. It has two parameters: one to tell the function which turtle to move around,
and the other to tell it the size of the square we want drawn. Make sure you know where the body of the function ends
— it depends on the indentation, and the blank lines don’t count for this purpose!

Docstrings for documentation


If the first thing after the function header is a string, it is treated as a docstring and gets special treatment in Python
and in some programming tools.
Docstrings are the key way to document our functions in Python and the documentation part is important. Because
whoever calls our function shouldn’t have to need to know what is going on in the function or how it works; they
just need to know what arguments our function takes, what it does, and what the expected result is. Enough to be
able to use the function without having to look underneath. This goes back to the concept of abstraction of which
we’ll talk more about.
Docstrings are usually formed using triple-quoted strings as they allow us to easily expand the docstring later on
should we want to write more than a one-liner.
Just to differentiate from comments, a string at the start of a function (a docstring) is retrievable by Python tools at
runtime. By contrast, comments are completely eliminated when the program is parsed.

64 Chapter 4. Functions
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

Defining the function just tells Python how to do a particular task, not to perform it. In order to execute a function
we need to make a function call. We’ve already seen how to call some built-in functions like print, range and
int. Function calls contain the name of the function being executed followed by a list of values, called arguments,
which are assigned to the parameters in the function definition. So in the second last line of the program, we call the
function, and pass alex as the turtle to be manipulated, and 50 as the size of the square we want. While the
function is executing, then, the variable size refers to the value 50, and the variable animal refers to the same
turtle instance that the variable alex refers to. We called it animal to signify that there is no meaning to the name
you give a function argument.
Once we’ve defined a function, we can call it as often as we like, and its statements will be executed each time we
call it. And we could use it to get any of our turtles to draw a square. In the next example, we’ve changed the
draw_square function a little, and we get tess to draw 15 squares, with some variations.
1 import turtle
2

3 def draw_multicolor_square(animal, size):


4 """Make animal draw a multi-color square of given size."""
5 for color in ["red", "purple", "hotpink", "blue"]: [Link](color)
6 [Link](size) [Link](90)
7

10 window = [Link]() [Link]("lightgreen")


# Set up the window and its attributes
11

12

13 tess = [Link]() [Link](3)


# Create tess and set some attributes
14

15

16 size = 20 # Size of the smallest square


17 for _ in range(15):
18 draw_multicolor_square(tess, size)
19 size += 10 # Increase the size for next time
20 [Link](10) # Move tess along a little
21 [Link](18) # and give her some turn
22

23 [Link]()

Functions can call other functions

Let’s assume now we want a function to draw a rectangle. We need to be able to call the function with different
arguments for width and height. And, unlike the case of the square, we cannot repeat the same thing 4 times,
because the four sides are not equal.
So we eventually come up with this rather nice code that can draw a rectangle.

Functions can call other functions 65


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

1 def draw_rectangle(animal, width, height):


2 """Get animal to draw a rectangle of given width and height."""
3 for _ in range(2):
4 [Link](width)
5 [Link](90)
6 [Link](height)
7 [Link](90)

Thinking like a scientist involves looking for patterns and relationships. In the code above, we’ve done that to some
extent. We did not just draw four sides. Instead, we spotted that we could draw the rectangle as two halves, and used
a loop to repeat that pattern twice.
But now we might spot that a square is a special kind of rectangle. We already have a function that draws a
rectangle, so we can use that to draw our square.
1 def draw_square(animal, size): # A new version of draw_square
2 draw_rectangle(animal, size, size)

There are some points worth noting here:


• Functions can call other functions.
• Rewriting draw_square like this captures the relationship that we’ve spotted between squares and
rectangles.
• A caller of this function might say draw_square(tess, 50). The parameters of this function, animal
and size, are assigned the values of the tess object, and the int 50 respectively.
• In the body of the function they are just like any other variable.
• When the call is made to draw_rectangle, the values in variables animal and size are fetched first,
then the call happens. So as we enter the top of function draw_rectangle, its variable animal is
assigned the tess object, and width and height in that function are both given the value 50.
So far, it may not be clear why it is worth the trouble to create all of these new functions. Actually, there are a lot of
reasons, but this example demonstrates two:
1. Creating a new function gives us an opportunity to name a group of statements. Functions can simplify a
program by hiding a complex computation behind a single command. The function (including its name) can
capture our mental chunking, or abstraction, of the problem.
2. Creating a new function can make a program smaller by eliminating repetitive code.
As we might expect, we have to create a function before we can execute it. In other words, the function definition
has to be executed before the function is called.

Flow of execution

In order to ensure that a function is defined before its first use, we have to know the order in which statements are
executed, which is called the flow of execution.
Execution always begins at the first statement of the program. Statements are executed one at a time, in order from
top to bottom.
Function definitions do not alter the flow of execution of the program, but remember that statements inside the
function are not executed until the function is called. Although it is not common, we can define one function inside
another. In this case, the inner definition isn’t executed until the outer function is called.
Function calls are like a detour in the flow of execution. Instead of going to the next statement, the flow jumps to the
first line of the called function, executes all the statements there, and then comes back to pick up where it left off.

66 Chapter 4. Functions
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

That sounds simple enough, until we remember that one function can call another. While in the middle of one
function, the program might have to execute the statements in another function. But while executing that new
function, the program might have to execute yet another function!
Fortunately, Python is adept at keeping track of where it is, so each time a function completes, the program picks up
where it left off in the function that called it. When it gets to the end of the program, it terminates.
What’s the moral of this sordid tale? When we read a program, don’t read from top to bottom. Instead, follow the
flow of execution.
As a simple example, let’s consider the following program:

1
import turtle
2

3
def draw_square(animal, size):
4
for _ in range(4): [Link](size) [Link](90)
5

6 window = [Link]()# Set up the window and its attributes


7

10 tess = [Link]() # Create tess and set some attributes


11

12 draw_square(tess, 50)
13

14 [Link]()

The Python interpreter reads this script line by line. At the first line the turtle module is imported. We then
define draw_square, which contains the instructions for a given turtle to draw a square. However, nothing
happens yet. We then go on to define a window, and our charming turtle tess. The next line calls
``draw_square, asking tess to draw a square with sides of length 50. Finally, [Link]()
actually runs these execu- tions, and you will see tess draw a square on the screen.
Being able to trace your program is a valuable skill for a programmer.

Functions that require arguments

Most functions require arguments: the arguments provide for generalization. For example, if we want to find the
absolute value of a number, we have to indicate what the number is. Python has a built-in function for computing the
absolute value:

>>> abs(5)
5
>>> abs(-5)
5

In this example, the arguments to the abs function are 5 and -5.
Some functions take more than one argument. For example the built-in function pow takes two arguments, the base
and the exponent. Inside the function, the values that are passed get assigned to variables called parameters.

>>> pow(2, 3)
8
>>> pow(7, 4)
2401

Another built-in function that takes more than one argument is max.

4.4. Functions that require arguments 67


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

>>> max(7, 11)


11
>>> max(4, 1, 17, 2, 12)
17
>>> max(3 * 11, 5**3, 512 - 9, 1024**0)
503

max can be passed any number of arguments, separated by commas, and will return the largest value passed. The
arguments can be either simple values or expressions. In the last example, 503 is returned, since it is larger than 33,
125, and 1.

Functions that return values

All the functions in the previous section return values. Calling each of these functions generates a value, which we
usually assign to a variable or use as part of an expression.
1 biggest = max(3, 7, 2, 5)
2 x = abs(3 - 11) + 10

So an important difference between these functions and one like draw_square is that draw_square was not
executed because we wanted it to compute a value — on the contrary, we wrote draw_square because we
wanted it to execute a sequence of steps that caused the turtle to draw.
A function that returns a value is called a fruitful function in this book. The opposite of a fruitful function is
void function — one that is not executed for its resulting value, but is executed because it does something useful.
(Languages like Java, C#, C and C++ use the term “void function”, other languages like Pascal call it a procedure.)
Even though void functions are not executed for their resulting value, Python always wants to return something. So
if the programmer doesn’t arrange to return a value, Python will automatically return the value None.
How do we write our own fruitful function? In the exercises at the end of chapter 2 we saw the standard formula for
compound interest, which we’ll now write as a fruitful function:

1 def final_amount(p, r, n, t):


2 """
3 Apply the compound interest formula to p
4 to produce the final amount.
5 """
6

7 a = p * (1 + r/n) ** (n*t)
8 return a # This is new, and makes the function fruitful.
9

10 # now that we have the function above, let us call it.


11 toInvest = float(input("How much do you want to invest?"))
(continues on next page)

68 Chapter 4. Functions
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

(continued from previous page)


12 fnl = final_amount(toInvest, 0.08, 12, 5)
13 print("At the end of the period you'll have",
fnl)
• The return statement is followed an expression (a in this case). This expression will be evaluated and
returned to the caller as the “fruit” of calling this function.
• We prompted the user for the principal amount. The type of toInvest is a string, but we need a number
before we can work with it. Because it is money, and could have decimal places, we’ve used the float type
converter function to parse the string and return a float.
• Notice how we entered the arguments for 8% interest, compounded 12 times per year, for 5 years.
• When we run this, we get the output
At the end of the period you’ll have 14898.457083
This is a bit messy with all these decimal places, but remember that Python doesn’t understand that we’re
working with money: it just does the calculation to the best of its ability, without rounding. Later we’ll see
how to format the string that is printed in such a way that it does get nicely rounded to two decimal places
before printing.
• The line toInvest = float(input("How much do you want to invest?")) also shows
yet another example of composition — we can call a function like float, and its arguments can be the results
of other function calls (like input) that we’ve called along the way.
Notice something else very important here. The name of the variable we pass as an argument — toInvest — has
nothing to do with the name of the parameter — p. It is as if p = toInvest is executed when final_amount
is called. It doesn’t matter what the value was named in the caller, in final_amount its name is p.
These short variable names are getting quite tricky, so perhaps we’d prefer one of these versions instead:

1 def final_amount_v2(principal_amount, nominal_percentage_rate,


2 num_times_per_year, years):
3 a = principal_amount * (1 + nominal_percentage_rate /
4 num_times_per_year) ** (num_times_per_year*years)
5 return a
6

7 def final_amount_v3(amount, rate, compounded, years):


8 a = amount * (1 + rate/compounded) ** (componded*years)
9 return a
10

11 def final_amount_v4(amount, rate, compounded, years):


12 """
13 The a in final_amount_v3 was a useless asignment.
14 We might as well skip it.
15 """
16 return amount * (1 + rate/compounded) ** (componded*years)

They all do the same thing. Use your judgement to write code that can be best understood by other humans! Short
variable names should generally be avoided, unless when short variables make more sense. This happens in
particular with mathematical equations, where it’s perfectly fine to use x, y, etc.

Variables and parameters are local

When we create a local variable inside a function, it only exists inside the function, and we cannot use it outside.
For example, consider again this function:

4.6. Variables and parameters are local 69


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

1 def final_amount(p, r, n, t):


2 a = p * (1 + r/n) **
3 (n*t) return a

If we try to use a, outside the function, we’ll get an error:

>>> a
NameError: name 'a' is not defined

The variable a is local to final_amount, and is not visible outside the function.
Additionally, a only exists while the function is being executed — we call this its lifetime. When the execution of
the function terminates, the local variables are destroyed.
Parameters are also local, and act like local variables. For example, the lifetimes of p, r, n, t begin when
final_amount is called, and the lifetime ends when the function completes its execution.
So it is not possible for a function to set some local variable to a value, complete its execution, and then when it
is called again next time, recover the local variable. Each call of the function creates new local variables, and their
lifetimes expire when the function returns to the caller.
Return values

The built-in functions we have used, such as abs, pow, int, max, and range, have produced results. Calling each
of these functions generates a value, which we usually assign to a variable or use as part of an expression.

1 biggest = max(3, 7, 2, 5)
2 x = abs(3 - 11) + 10

We also wrote our own function to return the final amount for a compound interest calculation.
In this chapter, we are going to write more functions that return values, which we will call fruitful functions, for want
of a better name. The first example is area, which returns the area of a circle with the given radius:

74 Chapter 4. Functions
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

1 def area(radius):
2 b = 3.14159 * radius**2
3 return b

We have seen the return statement before, but in a fruitful function the return statement includes a return
value. This statement means: evaluate the return expression, and then return it immediately as the result (the fruit) of
this function. The expression provided can be arbitrarily complicated, so we could have written this function like
this:
1 def area(radius):
2 return 3.14159 * radius * radius

On the other hand, temporary variables like b above often make debugging easier.
Sometimes it is useful to have multiple return statements, one in each branch of a conditional. We have already seen
the built-in abs, now we see how to write our own:
1 def absolute_value(x):
2 if x < 0:
3 return -x
4 else:
5 return x

Another way to write the above function is to leave out the else and just follow the if condition by the second
return statement.
1 def absolute_value(x):
2 if x < 0:
3 return -x
4 return x

Think about this version and convince yourself it works the same as the first one.
Code that appears after a return statement, or any other place the flow of execution can never reach, is called dead
code, or unreachable code.
In a fruitful function, it is a good idea to ensure that every possible path through the program hits a return
statement. The following version of absolute_value fails to do this:
1 def bad_absolute_value(x):
2 if x < 0:
3 return -x
4 elif x > 0:
5 return x

This version is not correct because if x happens to be 0, neither condition is true, and the function ends without
hitting a return statement. In this case, the return value is a special value called None:
>>> print(bad_absolute_value(0))
None

All Python functions return None whenever they do not return another value.
It is also possible to use a return statement in the middle of a for loop, in which case control immediately returns
from the function. Let us assume that we want a function which looks through a list of words. It should return the
first 2-letter word. If there is not one, it should return the empty string:
1 def find_first_2_letter_word(words):
2 for word in words:
(continues on next page)

4.10. Return values 75


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

(continued from previous page)


3 if len(word) == 2:
4 return word
5 return ""

>>> find_first_2_letter_word(["This", "is", "a", "dead",


"parrot"]) 'is'
>>> find_first_2_letter_word(["I", "like", "cheese"])
''

Single-step through this code and convince yourself that in the first test case that we’ve provided, the function
returns while processing the second element in the list: it does not have to traverse the whole list.

Program development

At this point, you should be able to look at complete functions and tell what they do. Also, if you have been doing
the exercises, you have written some small functions. As you write larger functions, you might start to have more
difficulty, especially with runtime and semantic errors.
To deal with increasingly complex programs, we are going to suggest a technique called incremental development.
The goal of incremental development is to avoid long debugging sessions by adding and testing only a small amount
of code at a time.
As an example, suppose we want to find the distance between two points, given by the coordinates (x 1, y1) and (x2,
y2). By the Pythagorean theorem, the distance is:

The first step is to consider what a distance function should look like in Python. In other words, what are the
inputs (parameters) and what is the output (return value)?
In this case, the two points are the inputs, which we can represent using four parameters. The return value is the
distance, which is a floating-point value.
Already we can write an outline of the function that captures our thinking so far:

1 def distance(x1, y1, x2, y2):


2 return 0.0

Obviously, this version of the function doesn’t compute distances; it always returns zero. But it is syntactically
correct, and it will run, which means that we can test it before we make it more complicated.
To test the new function, we call it with sample values:

>>> distance(1, 2, 4, 6)
0.0

We chose these values so that the horizontal distance equals 3 and the vertical distance equals 4; that way, the result
is 5 (the hypotenuse of a 3-4-5 triangle). When testing a function, it is useful to know the right answer.
At this point we have confirmed that the function is syntactically correct, and we can start adding lines of code.
After each incremental change, we test the function again. If an error occurs at any point, we know where it must be
— in the last line we added.
A logical first step in the computation is to find the differences x2- x1 and y2- y1. We will refer to those values using
temporary variables named dx and dy.

76 Chapter 4. Functions
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

1 def distance(x1, y1, x2, y2):


2 dx = x2 - x1
3 dy = y2 - y1
4 return 0.0

If we call the function with the arguments shown above, when the flow of execution gets to the return statement, dx
should be 3 and dy should be 4. We can check this by running the function and printing the returned
variable. Next we compute the sum of squares of dx and dy:

1 def distance(x1, y1, x2, y2):


2 dx = x2 - x1
3 dy = y2 - y1
4 dsquared = dx*dx + dy*dy
5 return 0.0

Again, we could run the program at this stage and check the value of dsquared (which should be 25).
Finally, using the fractional exponent 0.5 to find the square root, we compute and return the result:

1 def distance(x1, y1, x2, y2):


2 dx = x2 - x1
3 dy = y2 - y1
4 dsquared = dx*dx + dy*dy
5 result = dsquared**0.5
6 return result

If that works correctly, you are done. Otherwise, you might want to inspect the value of result before the return
statement.
When you start out, you might add only a line or two of code at a time. As you gain more experience, you might find
yourself writing and debugging bigger conceptual chunks. Either way, stepping through your code one line at a time
and verifying that each step matches your expectations can save you a lot of debugging time. As you improve your
programming skills you should find yourself managing bigger and bigger chunks: this is very similar to the way we
learned to read letters, syllables, words, phrases, sentences, paragraphs, etc., or the way we learn to chunk music —
from individual notes to chords, bars, phrases, and so on.
The key aspects of the process are:
1. Start with a working skeleton program and make small incremental changes. At any point, if there is an error,
you will know exactly where it is.
2. Use temporary variables to refer to intermediate values so that you can easily inspect and check them.
3. Once the program is working, relax, sit back, and play around with your options. (There is interesting research
that links “playfulness” to better understanding, better learning, more enjoyment, and a more positive mindset
about what you can achieve — so spend some time fiddling around!) You might want to consolidate multiple
statements into one bigger compound expression, or rename the variables you’ve used, or see if you can make
the function shorter. A good guideline is to aim for making code as easy as possible for others to read.
Here is another version of the function. It makes use of a square root function that is in the math module (we’ll
learn about modules shortly). Which do you prefer? Which looks “closer” to the Pythagorean formula we started out
with?

1 import math
2

3 def distance(x1, y1, x2, y2):


4 return [Link]( (x2-x1)**2 + (y2-y1)**2 )

4.11. Program development 77


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

>>> distance(1, 2, 4, 6)
5.0

Debugging with print

A powerful technique for debugging, is to insert extra print functions in carefully selected places in your code.
Then, by inspecting the output of the program, you can check whether the algorithm is doing what you expect it to.
Be clear about the following, however:
• You must have a clear solution to the problem, and must know what should happen before you can debug a
program. Work on solving the problem on a piece of paper (perhaps using a flowchart to record the steps you
take) before you concern yourself with writing code. Writing a program doesn’t solve the problem — it simply
automates the manual steps you would take. So first make sure you have a pen-and-paper manual solution that
works. Programming then is about making those manual steps happen automatically.
• Do not write chatterbox functions. A chatterbox is a fruitful function that, in addition to its primary task, also
asks the user for input, or prints output, when it would be more useful if it simply shut up and did its work
quietly.
For example, we’ve seen built-in functions like range, max and abs. None of these would be useful
building blocks for other programs if they prompted the user for input, or printed their results while they
performed their tasks.
So a good tip is to avoid calling print and input functions inside fruitful functions, unless the primary
purpose of your function is to perform input and output. The one exception to this rule might be to temporarily
sprinkle some calls to print into your code to help debug and understand what is happening when the code
runs, but these will then be removed once you get things working.

Composition

As you should expect by now, you can call one function from within another. This ability is called composition.
As an example, we’ll write a function that takes two points, the center of the circle and a point on the perimeter, and
computes the area of the circle.
Assume that the center point is stored in the variables xc and yc, and the perimeter point is in xp and yp. The
first step is to find the radius of the circle, which is the distance between the two points. Fortunately, we’ve just
written a function, distance, that does just that, so now all we have to do is use it:

1 radius = distance(xc, yc, xp, yp)

The second step is to find the area of a circle with that radius and return it. Again we will use one of our earlier
functions:
1 result = area(radius)
2 return result

Wrapping that up in a function, we get:

1 def area_of_circle(xc, yc, xp, yp):


2 radius = distance(xc, yc, xp, yp)
3 result = area(radius)
4 return result

78 Chapter 4. Functions
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

The temporary variables radius and result are useful for development, debugging, and single-stepping
through the code to inspect what is happening, but once the program is working, we can make it more concise by
composing the function calls:

1 def area_of_circle(xc, yc, xp, yp):


2 return area(distance(xc, yc, xp, yp))

Boolean functions

Functions can return Boolean values, which is often convenient for hiding complicated tests inside functions. For
example:
1 def is_divisible(x, y):
2 """ Test if x is exactly divisible by y """
3 if x % y == 0:
4 return True
5 else:
6 return False

It is common to give Boolean functions names that sound like yes/no questions. is_divisible returns either
True or False to indicate whether the x is or is not divisible by y.
We can make the function more concise by taking advantage of the fact that the condition of the if statement is
itself a Boolean expression. We can return it directly, avoiding the if statement altogether:

1 def is_divisible(x, y):


2 return x % y == 0

This session shows the new function in action:

>>> is_divisible(6, 4)
False
>>> is_divisible(6, 3)
True

Boolean functions are often used in conditional statements:

1 if is_divisible(x, y):
2 ... # Do something ...
3 else:
4 ... # Do something else ...

It might be tempting to write something like:

1 if is_divisible(x, y) == True:

but the extra comparison is unnecessary.

Local variables

Functions are called, or activated, and while they’re busy they create their own stack frame which holds local
variables. A local variable is one that belongs to the current activation. As soon as the function returns (whether from
an explicit return statement or because Python reached the last statement), the stack frame and its local variables are
all destroyed. The important consequence of this is that a function cannot use its own variables to remember any
kind of state between different activations. It cannot count how many times it has been called, or remember to
switch colors between red and blue UNLESS it makes use of variables that are global. Global variables will survive
even after our function has exited, so they are the correct way to maintain information between calls.
1 sz = 2
def h2():
2

This fragment assumes our turtle is tess. Each time we call h2() it turns, draws, and increases the global
variable sz. Python always assumes that an assignment to a variable (as in line 7) means that we want a new local
variable, unless we’ve provided a global declaration (on line 4). So leaving out the global declaration means this
does not work.

Tip: Local variables do not survive when you exit the function
Use a Python visualizer like the one at [Link] to build a strong understanding of func-
tion calls, stack frames, local variables, and function returns.

Tip: Assignment in a function creates a local variable


Any assignment to a variable within a function means Python will make a local variable, unless we override with
global.

String handling

There are only four really important operations on strings, and we’ll be able to do just about anything. There are
many more nice-to-have methods (we’ll call them sugar coating) that can make life easier, but if we can work with
the basic four operations smoothly, we’ll have a great grounding.

4.20. Local variables 87


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

• len(str) finds the length of a string.


• str[i] the subscript operation extracts the i’th character of the string, as a new string.
• str[i:j] the slice operation extracts a substring out of a string.
• [Link](target) returns the index where target occurs within the string, or -1 if it is not
found. So if we need to know if “snake” occurs as a substring within s, we could write

1 if [Link]("snake") >= 0: ...


2 if "snake" in s: ... # Also works, nice-to-know sugar coating!

It would be wrong to split the string into words unless we were asked whether the word “snake” occurred in the string.
Suppose we’re asked to read some lines of data and find function definitions, e.g.: def
some_function_name(x, y):, and we are further asked to isolate and work with the name of the func-
tion. (Let’s say, print it.)

1 s = "..." # Get the next line from somewhere # Look for "def " in the li
2 def_pos = [Link]("def ") # If it occurs at the left margin
3 if def_pos == 0: op_index = [Link]("(")
# Find fnname
the index
= s[4:op_index]
of the open print(fnname)
parenthesis # Slice out the funct
4 # ... and work with it.
5

One can extend these ideas:


• What if the function def was indented, and didn’t start at column 0? The code would need a bit of adjustment,
and we’d probably want to be sure that all the characters in front of the def_pos position were spaces. We
would not want to do the wrong thing on data like this: # I def initely like Python!
• We’ve assumed on line 3 that we will find an open parenthesis. It may need to be checked that we did!
• We have also assumed that there was exactly one space between the keyword def and the start of the
function name. It will not work nicely for def f(x)
As we’ve already mentioned, there are many more “sugar-coated” methods that let us work more easily with strings.
There is an rfind method, like find, that searches from the end of the string backwards. It is useful if we want
to find the last occurrence of something. The lower and upper methods can do case conversion. And the
split method is great for breaking a string into a list of words, or into a list of lines. We’ve also made extensive
use in this book of the format method. In fact, if we want to practice reading the Python documentation and
learning some new methods on our own, the string methods are an excellent resource.
Exercises:
• Suppose any line of text can contain at most one url that starts with “[Link] and ends at the next space in the
line. Write a fragment of code to extract and print the full url if it is present. (Hint: read the documentation for
find. It takes some extra arguments, so you can set a starting point from which it will search.)
• Suppose a string contains at most one substring “< . . . >”. Write a fragment of code to extract and print the
portion of the string between the angle brackets.

Looping and lists

Computers are useful because they can repeat computation, accurately and fast. So loops are going to be a central
feature of almost all programs you encounter.

Tip: Don’t create unnecessary lists

88 Chapter 4. Functions
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

Lists are useful if you need to keep data for later computation. But if you don’t need lists, it is probably better not to
generate them.

Here are two functions that both generate ten million random numbers, and return the sum of the numbers. They both
work.
1 import random
2 joe = [Link]()
3

4 def sum1():
5 """ Build a list of random numbers, then sum them """
6 xs = []
7 for i in range(10000000):
8 num = [Link](1000) # Generate one random number
9 [Link](num) # Save it in our list
10

11 tot = sum(xs)
12 return tot
13

14 def sum2():
15 """ Sum the random numbers as we generate them """
16 tot = 0
17 for i in range(10000000):
18 num = [Link](1000)
19 tot += num
20 return tot
21

22 print(sum1())
23 print(sum2())

What reasons are there for preferring the second version here? (Hint: open a tool like the Performance Monitor on
your computer, and watch the memory usage. How big can you make the list before you get a fatal memory error in
sum1?)
In a similar way, when working with files, we often have an option to read the whole file contents into a single
string, or we can read one line at a time and process each line as we read it. Line-at-a-time is the more traditional
and perhaps safer way to do things — you’ll be able to work comfortably no matter how large the file is. (And, of
course, this mode of processing the files was essential in the old days when computer memories were much smaller.)
But you may find whole-file-at-once is sometimes more convenient!

4.22. Looping and lists 89


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

90 Chapter 4. Functions
CHAPTER 5

Data Types

Strings

A compound data type

So far we have seen built-in types like int, float, bool, str and we’ve seen lists and pairs. Strings, lists, and
pairs are qualitatively different from the others because they are made up of smaller pieces. In the case of strings,
they’re made up of smaller strings each containing one character.
Types that comprise smaller pieces are called compound data types. Depending on what we are doing, we may
want to treat a compound data type as a single thing, or we may want to access its parts. This ambiguity is useful.

Working with strings as single things

We previously saw that each turtle instance has its own attributes and a number of methods that can be applied to the
instance. For example, we could set the turtle’s color, and we wrote [Link](90).
Just like a turtle, a string is also an object. So each string instance has its own attributes and methods.
For example:

>>> our_string = "Hello, World!"


>>> all_caps = our_string.upper()
>>> all_caps
'HELLO, WORLD!'

upper is a method that can be invoked on any string object to create a new string, in which all the characters are in
uppercase. (The original string our_string remains unchanged.)
There are also methods such as lower, capitalize, and swapcase that do other interesting stuff.
To learn what methods are available, you can consult the Help documentation, look for string methods, and read the
documentation. Or, if you’re a bit lazier, simply type the following into an editor like Spyder or PyScripter script:

91
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

1 our_string = "Hello, World!"


2 new_string = our_string.

When you type the period to select one of the methods of our_string, your editor might pop up a selection window
— typically by pressing Tab — showing all the methods (there are around 70 of them — thank goodness we’ll
only use a few of those!) that could be used on your string.

When you type the name of the method, some further help about its parameter and return type, and its docstring,
may be displayed by your scripting environments (for instance, in a Jupyter notebook you can get this inofrmation
by pressing Shift+Tab after a function name).

92 Chapter 5. Data Types


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

Working with the parts of a string

The indexing operator (Python uses square brackets to enclose the index) selects a single character substring from a
string:

>>> fruit = "banana"


>>> letter = fruit[1]
>>> print(letter)

The expression fruit[1] selects character number 1 from fruit, and creates a new string containing just this
one character. The variable letter refers to the result. When we display letter, we could get a surprise:

Computer scientists always start counting from zero! The letter at subscript position zero of "banana" is b. So at
position [1] we have the letter a.
If we want to access the zero-eth letter of a string, we just place 0, or any expression that evaluates to 0, inbetween
the brackets:

>>> letter = fruit[0]


>>> print(letter)
b

The expression in brackets is called an index. An index specifies a member of an ordered collection, in this case the
collection of characters in the string. The index indicates which one you want, hence the name. It can be any integer
expression.
We can use enumerate to visualize the indices:

>>> fruit = "banana"


>>> list(enumerate(fruit))
[(0, 'b'), (1, 'a'), (2, 'n'), (3, 'a'), (4, 'n'), (5, 'a')]

Do not worry about enumerate at this point, we will see more of it in the chapter on lists.
Note that indexing returns a string — Python has no special type for a single character. It is just a string of length 1.
We’ve also seen lists previously. The same indexing notation works to extract elements from a list:

>>> prime_numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
>>> prime_numbers[4]
11
>>> friends = ["Joe", "Zoe", "Brad", "Angelina", "Zuki", "Thandi", "Paris"]
>>> friends[3]
'Angelina'

Length

The len function, when applied to a string, returns the number of characters in a string:

>>> word = "banana"


>>> len(word)
6

To get the last letter of a string, you might be tempted to try something like this:

5.1. Strings 93
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

1 size = len(word) last = word[size]


2 # ERROR!

That won’t work. It causes the runtime error IndexError: string index out of range. The reason is
that there is no character at index position 6 in "banana". Because we start counting at zero, the six indexes are
numbered 0 to 5. To get the last character, we have to subtract 1 from the length of word:

1 size = len(word)
2 last = word[size-1]

Alternatively, we can use negative indices, which count backward from the end of the string. The expression
word[-1] yields the last letter, word[-2] yields the second to last, and so on.
As you might have guessed, indexing with a negative index also works like this for lists.

Traversal and the for loop

A lot of computations involve processing a string one character at a time. Often they start at the beginning, select
each character in turn, do something to it, and continue until the end. This pattern of processing is called a traversal.
One way (a very bad way) to encode a traversal is with a while statement:
1 ix = 0
2 while ix <
3 len(fruit):
4 letter =
5 fruit[ix]
print(letter)
This loop traverses the string and displays each letter on a line by itself. It uses ix for the index, which does not
make it any clearer. The loop condition is ix < len(fruit), so when ix is equal to the length of the string,
the condition is false, and the body of the loop is not executed. The last character accessed is the one with the index
len(fruit)-1, which is the last character in the string. However, this code is a lot longer than it needs to be, and
not very clear at all.
But we’ve previously seen how the for loop can easily iterate over the elements in a list and it can do so for strings
as well:

1 word="Banana"
2 for letter in word:
3 print(letter)

Each time through the loop, the next character in the string is assigned to the variable c. The loop continues until no
characters are left. Here we can see the expressive power the for loop gives us compared to the while loop when
traversing a string.
The following example shows how to use concatenation and a for loop to generate an abecedarian series.
Abecedarian refers to a series or list in which the elements appear in alphabetical order. For example, in Robert
McCloskey’s book Make Way for Ducklings, the names of the ducklings are Jack, Kack, Lack, Mack, Nack, Ouack,
Pack, and Quack. This loop outputs these names in order:
1 prefixes = "JKLMNOPQ"
2 suffix = "ack"
3

4 for p in prefixes:
5 print(p + suffix)

The output of this program is:

94 Chapter 5. Data Types


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

Jac
k
Kac
k
Lac
k
Mac
k
Nac
Of course, that’s not quite right because Ouack and Quack are misspelled. You’ll fix this as an exercise below.

Slices

A substring of a string is obtained by taking a slice. Similarly, we can slice a list to refer to some sublist of the items
in the list:

>>> phrase = "Pirates of the Caribbean"


>>> print(phrase[0:7])
Pirates
>>> print(phrase[11:14])
the
>>> print(phrase[13:24])
e Caribbean
>>> friends = ["Joe", "Zoe", "Brad", "Angelina", "Zuki", "Thandi", "Paris"]
>>> print(friends[2:4])
['Brad', 'Angelina']

The operator [n:m] returns the part of the string from the n’th character to the m’th character, including the first
but excluding the last. This behavior makes sense if you imagine the indices pointing between the characters, as in
the following diagram:

If you imagine this as a piece of paper, the slice operator [n:m] copies out the part of the paper between the n and m
positions. Provided m and n are both within the bounds of the string, your result will be of length (m-n).
Three tricks are added to this: if you omit the first index (before the colon), the slice starts at the beginning of the
string (or list). If you omit the second index, the slice extends to the end of the string (or list). Similarly, if you
provide value for n that is bigger than the length of the string (or list), the slice will take all the values up to the end.
(It won’t give an “out of range” error like the normal indexing operation does.) Thus:

>>> word = "banana"


>>> word[:3]
'ban'
>>> word[3:]
'ana'
>>> word[3:999]
'ana'

What do you think phrase[:] means? What about friends[4:]? phrase[-5:-3]?

5.1. Strings 95
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

String comparison

The comparison operators work on strings. To see if two strings are equal:
1 if word == "banana":
2 print("Yes, we have no bananas!")

Other comparison operations are useful for putting words in lexicographical order:
1 if word < "banana":
2 print("Your word, " + word + ", comes before banana.")
3 elif word > "banana":
4 print("Your word, " + word + ", comes after banana.")
5 else:
6 print("Yes, we have no bananas!")

This is similar to the alphabetical order you would use with a dictionary, except that all the uppercase letters come
before all the lowercase letters. As a result:
Your word, Zebra, comes before banana.

A common way to address this problem is to convert strings to a standard format, such as all lowercase, before
performing the comparison. A more difficult problem is making the program realize that zebras are not fruit.

Strings are immutable

It is tempting to use the [] operator on the left side of an assignment, with the intention of changing a character in a
string. For example:
1 greeting = "Hello, world!"
2 greeting[0] = 'J' # ERROR!
3 print(greeting)

Instead of producing the output Jello, world!, this code produces the runtime error TypeError: 'str'
object does not support item assignment.
Strings are immutable, which means you can’t change an existing string. The best you can do is create a new string
that is a variation on the original:
1 greeting = "Hello, world!"
2 new_greeting = "J" + greeting[1:]
3 print(new_greeting)

The solution here is to concatenate a new first letter onto a slice of greeting. This operation has no effect on the
original string.

The in and not in operators

The in operator tests for membership. When both of the arguments to in are strings, in checks whether the left
argument is a substring of the right argument.

>>> "p" in "apple"


True
>>> "i" in "apple"
False
(continues on next page)

96 Chapter 5. Data Types


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

(continued from previous page)


>>> "ap" in "apple"
True
>>> "pa" in "apple"
False

Note that a string is a substring of itself, and the empty string is a substring of any other string. (Also note that
computer scientists like to think about these edge cases quite carefully!)

>>> "a" in "a"


True
>>> "apple" in "apple"
True
>>> "" in "a"
True
>>> "" in "apple"
True

The not in operator returns the logical opposite results of in:

>>> "x" not in "apple"


True

Combining the in operator with string concatenation using +, we can write a function that removes all the vowels
from a string:

1 def remove_vowels(phrase):
2 vowels = "aeiou"
3 string_sans_vowels = ""
4 for letter in phrase:
5 if [Link]() not in vowels:
6 string_sans_vowels += letter
7 return string_sans_vowels

Important to note is the [Link]() in line 5, without it, any uppercase vowels would not be removed.

A find function

What does the following function do?

1 def my_find(haystack, needle):


2 """
3 Find and return the index of needle in haystack.
4 Return -1 if needle does not occur in haystack.
5 """
6 for index, letter in enumerate(haystack):
7 if letter == needle:
8 return index
9 return -1

Compare the output of the code above with what Python does itself with the code below:

1 haystack = "Bananarama!"
2 print([Link]('a'))
3 print(my_find(haystack,'a'))

5.1. Strings 97
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

In a sense, find is the opposite of the indexing operator. Instead of taking an index and extracting the
corresponding character, it takes a character and finds the index where that character appears. If the character is not
found, the function returns -1.
This is another example where we see a return statement inside a loop. If letter == needle, the function
returns immediately, breaking out of the loop prematurely.
If the character doesn’t appear in the string, then the program exits the loop normally and returns -1.
This pattern of computation is sometimes called a eureka traversal or short-circuit evaluation, because as soon as
we find what we are looking for, we can cry “Eureka!”, take the short-circuit, and stop looking.

Looping and counting

The following program counts the number of times the letter a appears in a string, and is another example of the
counter pattern introduced in Counting digits:

1 def count_a(text):
2 count = 0
3 for letter in text:
4 if letter == "a":
5 count += 1
6 return(count)
7

8 print(count_a("banana") == 3)

Optional parameters

To find the locations of the second or third occurrence of a character in a string, we can modify the find function,
adding a third parameter for the starting position in the search string:

1 def find2(haystack, needle, start):


2 for index,letter in enumerate(haystack[start:])
3 if letter == needle:
4 return index + start
5 return -1
6

9 print(find2("banana", "a", 2) == 3)

The call find2("banana", "a", 2) now returns 3, the index of the first occurrence of “a” in “banana”
starting the search at index 2. What does find2("banana", "n", 3) return? If you said, 4, there is a good
chance you understand how find2 works.
Better still, we can combine find and find2 using an optional parameter:

1
def find(haystack, needle, start=0):
2
for index,letter in enumerate(haystack[start:]):
if letter == needle:
3
return index + start
4
return -1
5

When a function has an optional parameter, the caller may provide a matching argument. If the third argument is
provided to find, it gets assigned to start. But if the caller leaves the argument out, then start is given a default
value indicated by the assignment start=0 in the function definition.

98 Chapter 5. Data Types


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

So the call find("banana", "a", 2) to this version of find behaves just like find2, while in the call
find("banana", "a"), start will be set to the default value of 0.
Adding another optional parameter to find makes it search from a starting position, up to but not including the end
position:

1 def find(haystack, needle, start=0, end=-1):


2 for index,letter in enumerate(haystack[start:end])
3 if letter == needle:
4 return index + start
5 return -1

The semantics of start and end in this function are precisely the same as they are in the range function.

The built-in find method

Now that we’ve done all this work to write a powerful find function, we can reveal that strings already have their
own built-in find method. It can do everything that our code can do, and more! Try all the examples listed above,
and check the results!
The built-in find method is more general than our version. It can find substrings, not just single characters:

>>> "banana".find("nan")
2
>>> "banana".find("na", 3)
4

Usually we’d prefer to use the methods that Python provides rather than reinvent our own equivalents. But many of
the built-in functions and methods make good teaching exercises, and the underlying techniques you learn are your
building blocks to becoming a proficient programmer.

The split method

One of the most useful methods on strings is the split method: it splits a single multi-word string into a list of
individual words, removing all the whitespace between them. (Whitespace means any tabs, newlines, or spaces.)
This allows us to read input as a single string, and split it into words.

>>> phrase = "Well I never did said Alice"


>>> words = [Link]()
>>> words
['Well', 'I', 'never', 'did', 'said', 'Alice']

Cleaning up your strings

We’ll often work with strings that contain punctuation, or tab and newline characters, especially, as we’ll see in a
future chapter, when we read our text from files or from the Internet. But if we’re writing a program, say, to count
word frequencies or check the spelling of each word, we’d prefer to strip off these unwanted characters.
We’ll show just one example of how to strip punctuation from a string. Remember that strings are immutable, so
we cannot change the string with the punctuation — we need to traverse the original string and create a new string,
omitting any punctuation:

5.1. Strings 99
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

1 punctuation = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
2

3 def remove_punctuation(phrase):
4 phrase_sans_punct = ""
5 for letter in phrase:
6 if letter not in punctuation:
7 phrase_sans_punct += letter
8 return phrase_sans_punct

Setting up that first assignment is messy and error-prone. Fortunately, the Python string module already does it
for us. So we will make a slight improvement to this program — we’ll import the string module and use its
definition:
import string
1

2 def remove_punctuation(phrase):
3 phrase_sans_punct = ""
4 for letter in phrase:
5 if letter not in [Link]:
6 phrase_sans_punct += letter
7 return phrase_sans_punct
8

Try the examples below: “Well, I never did!”, said Alice. “Are you very, very, sure?”
Composing together this function and the split method from the previous section makes a useful combination —
we’ll clean out the punctuation, and split will clean out the newlines and tabs while turning the string into a list
of words:
1 my_story = """
2 Pythons are constrictors, which means that they will 'squeeze' the life
3 out of their prey. They coil themselves around their prey and with
4 each breath the creature takes the snake will squeeze a little tighter
5 until they stop breathing completely. Once the heart stops the prey
6 is swallowed whole. The entire animal is digested in the snake's
7 stomach except for fur or feathers. What do you think happens to the fur,
8 feathers, beaks, and eggshells? The 'extra stuff' gets passed out as ---
9 you guessed it --- snake POOP! """
10

11 words = remove_punctuation(my_story).split()
12 print(words)

The output:
['Pythons', 'are', 'constrictors', ... , 'it', 'snake', 'POOP']

There are other useful string methods, but this book isn’t intended to be a reference manual. On the other hand, the
Python Library Reference is. Along with a wealth of other documentation, it is available at the Python website.

The string format method

The easiest and most powerful way to format a string in Python 3 is to use the format method. To see how this
works, let’s start with a few examples:
1 phrase = "His name is {0}!".format("Arthur")
2 print(phrase)
3

4 name = "Alice"
(continues on next page)

100 Chapter 5. Data Types


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

(continued from previous page)


5 age = 10
6 phrase = "I am {1} and I am {0} years old.".format(age, name)
7 print(phrase)
8 phrase = "I am {0} and I am {1} years old.".format(age, name)
9 print(phrase)
10

11 x = 4
12 y = 5
13 phrase = "2**10 = {0} and {1} * {2} = {3:f}".format(2**10, x, y, x * y)
14 print(phrase)

Running the script produces:

His name is Arthur!


I am Alice and I am 10 years old.
I am 10 and I am Alice years old.
2**10 = 1024 and 4 * 5 =
20.000000
The template string contains place holders, ... {0} ... {1} ... {2} ... etc. The format method
substi- tutes its arguments into the place holders. The numbers in the place holders are indexes that determine which
argument gets substituted — make sure you understand line 6 above!
But there’s more! Each of the replacement fields can also contain a format specification — it is always introduced
by the : symbol (Line 13 above uses one.) This modifies how the substitutions are made into the template, and can
control things like:
• whether the field is aligned to the left <, center ^, or right >
• the width allocated to the field within the result string (a number like 10)
• the type of conversion (we’ll initially only force conversion to float, f, as we did in line 13 of the code above,
or perhaps we’ll ask integer numbers to be converted to hexadecimal using x)
• if the type conversion is a float, you can also specify how many decimal places are wanted (typically, .2f is
useful for working with currencies to two decimal places.)
Let’s do a few simple and common examples that should be enough for most needs. If you need to do anything more
esoteric, use help and read all the powerful, gory details.
1 name1 = "Paris"
2 name2 = "Whitney"
3 name3 = "Hilton"
4

5 print("Pi to three decimal places is {0:.3f}".format(3.1415926))


6 print("123456789 123456789 123456789 123456789 123456789 123456789")
7 print("|||{0:<15}|||{1:^15}|||{2:>15}|||Born in {3}|||"
8 .format(name1,name2,name3,1981))
9 print("The decimal value {0} converts to hex value {0:x}"
10 .format(123456))

This script produces the output:

Pi to three decimal places is 3.142


123456789 123456789 123456789 123456789 123456789 123456789
|||Paris ||| Whitney ||| Hilton|||Born in 1981|||
The decimal value 123456 converts to hex value 1e240

You can have multiple placeholders indexing the same argument, or perhaps even have extra arguments that are not
referenced at all:

Strings 101
How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd
Edition

1 letter = """
2 Dear {0} {2}.
3 {0}, I have an interesting money-making proposition for you!
4 If you deposit $10 million into my bank account, I can
5 double your money ...
6 """
7

8 print([Link]("Paris", "Whitney", "Hilton"))


9 print([Link]("Bill", "Henry", "Gates"))

This produces the following:


Dear Paris Hilton.
Paris, I have an interesting money-making proposition for you!
If you deposit $10 million into my bank account, I can
double your money ...

Dear Bill Gates.


Bill, I have an interesting money-making proposition for you!
If you deposit $10 million into my bank account I can
double your money ...

As you might expect, you’ll get an index error if your placeholders refer to arguments that you do not provide:
>>> "hello {3}".format("Dave")
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
IndexError: tuple index out of range

The following example illustrates the real utility of string formatting. First, we’ll try to print a table without using
string formatting:
1 print("i\ti**2\ti**3\ti**5\ti**10\ti**20")
2 for i in range(1, 11):
3 print(i, "\t", i**2, "\t", i**3, "\t", i**5, "\t",
4 i**10, "\t", i**20)

This program prints out a table of various powers of the numbers from 1 to 10. (This assumes that the tab width is
8. You might see something even worse than this if you tab width is set to 4.) In its current form it relies on the tab
character ( \t) to align the columns of values, but this breaks down when the values in the table get larger than the
tab width:
i i**2 i**3 i**5 i**10 i**20
1 1 1 1 1 1
2 4 8 32 1024 1048576
3 9 27 243 59049 3486784401
4 16 64 1024 1048576 1099511627776
5 25 125 3125 9765625 95367431640625
6 36 216 7776 60466176 3656158440062976
7 49 343 16807 282475249 79792266297612001
8 64 512 32768 1073741824 1152921504606846976
9 81 729 59049 3486784401 12157665459056928801
10 100 1000 100000 10000000000 100000000000000000000

One possible solution would be to change the tab width, but the first column already has more space than it needs.
The best solution would be to set the width of each column independently. As you may have guessed by now, string
formatting provides a much nicer solution. We can also right-justify each field:

102 Chapter 5. Data Types


How to Think Like a Computer Scientist: Learning with Python 3 Documentation, Release 3rd

Edition

layout
1 = "{0:>4}{1:>6}{2:>6}{3:>8}{4:>13}{5:>24}"
2

print([Link]("i",
3 "i**2", "i**3", "i**5", "i**10", "i**20"))
for
4 i in range(1, 11):
5 print([Link](i, i**2, i**3, i**5, i**10, i**20))

Running this version produces the following (much more satisfying) output:

i i**2 i**3 i**5 i**10 i**20


1 1 1 1 1 1
2 4 8 32 1024 1048576
3 9 27 243 59049 3486784401
4 16 64 1024 1048576 1099511627776
5 25 125 3125 9765625 95367431640625
6 36 216 7776 60466176 3656158440062976
7 49 343 16807 282475249 79792266297612001
8 64 512 32768 1073741824 1152921504606846976
9 81 729 59049 3486784401 12157665459056928801
10 100 1000 100000 10000000000 100000000000000000000

You might also like