3c. Prof. Rosenthal’s Review#
A review of Chapter 3: Functions, for students who have already studied the chapter. Use the two lists under Concepts covered as a checklist (click a list’s title to hide it), then work through the Questions, which go from easy to hard. Each question has a suggested answer under Answer. Try the question first, then open the Answer.
Concepts covered#
Python Syntax and Semantics
Defining and calling functions
This defines a function:
def FUNCTION_NAME(): BODY
defis a keyword. The first line is the header and must end with a colon. The body is one or more statements, indented (by convention, four spaces).Defining a function does not run its body. The body runs each time the function is called:
FUNCTION_NAME(). For example, this displaysHellotwice:def greet(): print('Hello') greet() greet()
A function must be defined before the line that calls it runs. Calling a name that has not been defined yet is a
NameError.Any legal variable name is a legal function name.
A function name without parentheses is a function object, not a call. In a notebook,
greetdisplays<function __main__.greet()>, and the body does not run.
Parameters and arguments
This defines a function with parameters:
def FUNCTION_NAME(PARAMETER1, PARAMETER2): BODY
FUNCTION_NAME(ARGUMENT1, ARGUMENT2)calls it: each argument’s value is assigned to the parameter in the same position, and then the body runs.An argument can be any expression, such as a variable or
'a' + 'b'. Its value is computed before the function runs.Calling a function with too few or too many arguments is a
TypeError.
Functions calling functions
A function’s body can call other functions. When a function calls another, it waits until that call ends, then continues with its next statement.
A function can call a function defined after it in the program, as long as both are defined before the first call runs.
for loops
This runs the indented body once for each value of
range(SOME_INTEGER):for VARIABLE in range(SOME_INTEGER): BODY
For example, this displays
0,1and2, on separate lines:for i in range(3): print(i)
range(SOME_INTEGER)is the integers0, 1, 2, ...up to SOME_INTEGER minus 1. The loop variable takes each value in turn, even if the body does not use it.The header of a
forloop ends with a colon, and its body is indented. A loop can be inside a function, and a function can be called inside a loop.
Local variables
Parameters, and variables assigned inside a function, are local: they exist only while the function runs. Using one outside the function is a
NameError.A local variable with the same name as a variable outside the function is a different variable: assigning to it does not change the one outside.
Printing
print(), with no arguments, displays an empty line.STRING * SOME_INTEGERmakes that many copies of the string, joined together:'-' * 5is'-----'.' ' * 3is three spaces.
Tracebacks
When a runtime error happens inside a function, the error message includes a traceback: the function calls in progress, from the first call at the top to the function where the error happened at the bottom, ending with the error itself.
Definitions, etc.
Definitions
DEFINITION: A function definition is a statement that creates a function.
DEFINITION: The header of a function is the first line of its definition.
DEFINITION: The body of a function is the sequence of statements inside its definition.
DEFINITION: A function object is the value created by a function definition. The name of the function is a variable that refers to it.
DEFINITION: A parameter is a name used inside a function to refer to the value passed as an argument.
DEFINITION: A loop is a statement that runs one or more statements, often repeatedly.
DEFINITION: A local variable is a variable defined inside a function, which can only be used inside that function.
DEFINITION: A stack diagram shows the functions that are running, with their variables and the values they refer to.
DEFINITION: A frame is a box in a stack diagram that represents one function call. It contains the function’s parameters and local variables. Variables created outside any function are in the frame called
__main__.DEFINITION: A traceback is the list of the functions that are running, displayed when an exception occurs.
Other ideas
Reasons to write functions:
A function gives a group of statements a name, which makes a program easier to read and debug.
Functions make a program shorter by removing repeated code; a later change is made in one place.
A long program divided into functions can be debugged one function at a time.
A well-designed function can be reused in other programs.
In a stack diagram, each function’s frame is below the frame of the function that called it; a traceback lists the functions in the same order.
Debugging is like experimental science: make a hypothesis about what is wrong, change the program, and check whether the result is what you predicted.
Start with a working program and make small changes, testing as you go. Spending a lot of time debugging is often a sign of writing too much code before testing it.
Questions#
The questions use functions, print, strings and for loops.
Question 1 (easy): what is displayed?#
Predict what this code displays, then open the Answer to check.
def greet():
print('Hello')
print('Goodbye')
greet()
greet()
Answer
Hello
Goodbye
Hello
Goodbye
Defining greet displays nothing. Each call runs the whole body, so the two lines are displayed twice.
Question 2 (easy): what is displayed?#
def first():
print('one')
def second():
print('two')
second()
print('three')
first()
Answer
two
three
one
The order of the definitions does not matter: a body runs only when its function is called, and the calls happen in the order second, print, first.
Question 3 (easy): what is displayed?#
def repeat(word, n):
print(word * n)
repeat('ab', 3)
repeat('-', 10)
Answer
ababab
----------
In the first call, word is 'ab' and n is 3; in the second, word is '-' and n is 10.
word * n makes n copies of word.
Question 4 (easy): what is displayed?#
for i in range(4):
print(i, i * i)
print('done')
Answer
0 0
1 1
2 4
3 9
done
range(4) is 0, 1, 2, 3, so the body runs four times with those values of i.
print('done') is not indented, so it is not part of the loop: it runs once, after the loop ends.
Question 5 (easy): write a function#
Write a function called underline that takes a string text and displays it, with a line of - characters below it, as long as the text.
Start from this header (replace ... with the body):
def underline(text):
...
Examples
underline('Hello')
Hello
-----
underline('Python 3')
Python 3
--------
Answer
def underline(text):
print(text)
print('-' * len(text))
Then try: underline('Hello').
len(text) is the number of characters in text, including spaces, and '-' * len(text) is that many dashes.
Question 6 (medium): what is displayed?#
def start():
print('start begins')
middle()
print('start ends')
def middle():
print('in middle')
start()
Answer
start begins
in middle
start ends
start calls middle, which is defined after start in the code. That is fine: by the time start() runs (the last line), both functions are defined.
start waits for middle to end, then goes on with its next statement.
Question 7 (medium): what is displayed?#
def show(x):
x = x + 1
print('inside:', x)
x = 10
show(x)
print('outside:', x)
Answer
inside: 11
outside: 10
The parameter x is a local variable of show, different from the x outside the function, even though they have the same name.
The call assigns 10 to the parameter, and x = x + 1 changes only the parameter.
Question 8 (medium): what is displayed?#
def stars(n):
print('*' * n)
for i in range(4):
stars(i + 1)
print()
stars(2)
Answer
*
**
***
****
**
i takes the values 0, 1, 2, 3, so stars is called with 1, 2, 3, 4.
print() with no arguments displays an empty line.
Question 9 (medium): what is displayed?#
What does this code display when it is run as a notebook cell? Does it display Hello?
def greet():
print('Hello')
greet
Answer
It does not display Hello. It displays something like:
<function __main__.greet()>
Without parentheses, greet is not a call: it is the function object that the name greet refers to, and the notebook displays it as the value of the last expression.
To run the body, call the function: greet().
Question 10 (medium): local variables and wrong calls#
Run this cell to define add_and_show.
def add_and_show(a, b):
total = a + b
print(a, '+', b, '=', total)
Two correct ways to call it:
add_and_show(2, 3)
2 + 3 = 5
add_and_show(10, -4)
10 + -4 = 6
For each of the three parts, predict what is displayed and which error happens.
Part a
add_and_show(1, 2)
print(total)
Part b
add_and_show(5)
Part c
add_and_show(1, 2, 3)
Answer
Part a: The call works and displays its line, and then print(total) fails with a NameError:
1 + 2 = 3
1 add_and_show(1, 2)
----> 2 print(total)
NameError: name 'total' is not defined
total is a local variable of add_and_show. It exists only while the function runs.
Part b: Too few arguments, so nothing is displayed (a TypeError):
----> 1 add_and_show(5)
TypeError: add_and_show() missing 1 required positional argument: 'b'
Part c: Too many arguments (a TypeError):
----> 1 add_and_show(1, 2, 3)
TypeError: add_and_show() takes 2 positional arguments but 3 were given
Question 11 (medium): write a function#
Write a function called print_right that takes a string text and an integer width, and displays text with enough spaces in front of it that its last character is in column width.
You can assume text has at most width characters.
Start from this header (replace ... with the body):
def print_right(text, width):
...
Examples
print_right('Monty', 10)
print_right('Python', 10)
print_right('Flying Circus', 20)
Monty
Python
Flying Circus
print_right('abc', 3)
print_right('abc', 4)
abc
abc
Answer
The number of spaces is width minus the length of the text:
def print_right(text, width):
spaces = width - len(text)
print(' ' * spaces + text)
Then try: print_right('Monty', 10), print_right('Python', 10) and print_right('Flying Circus', 20).
The same thing in one line, without the local variable spaces:
def print_right(text, width):
print(' ' * (width - len(text)) + text)
print(' ' * spaces, text) would not work exactly: print puts an extra space between its two values.
Question 12 (medium): refactor into a function#
This code displays two verses of a song. The verses are the same except for the animal and its sound.
print('Old MacDonald had a farm')
print('And on that farm he had a cow')
print('With a moo moo here')
print('Old MacDonald had a farm')
print('And on that farm he had a duck')
print('With a quack quack here')
Write a function verse that takes animal and sound and displays one verse, so that the code above can be replaced by two calls: verse('cow', 'moo') and verse('duck', 'quack').
Start from this header (replace ... with the body):
def verse(animal, sound):
...
Examples
verse('cow', 'moo')
Old MacDonald had a farm
And on that farm he had a cow
With a moo moo here
verse('pig', 'oink')
Old MacDonald had a farm
And on that farm he had a pig
With a oink oink here
Answer
def verse(animal, sound):
print('Old MacDonald had a farm')
print('And on that farm he had a', animal)
print('With a', sound, sound, 'here')
Then try: verse('cow', 'moo').
print separates its values with spaces, which is exactly what the lines need.
Another approach builds each line with + (note the spaces inside the strings):
def verse(animal, sound):
print('Old MacDonald had a farm')
print('And on that farm he had a ' + animal)
print('With a ' + sound + ' ' + sound + ' here')
Now a change to the verse (say, a new line) is made in one place instead of in every verse.
Question 13 (hard): what is displayed?#
def line(n):
print('-' * n)
def block(n, times):
for i in range(times):
line(n)
print('end of block')
def page():
block(3, 2)
block(5, 1)
page()
Answer
---
---
end of block
-----
end of block
page calls block(3, 2), which calls line(3) twice and then displays end of block.
Only after that call ends does page call block(5, 1), which calls line(5) once.
Question 14 (hard): tracebacks#
Run this cell to define inner and outer.
def inner(value):
print(value + 1)
def outer(value):
print('outer got', value)
inner(value)
Two correct ways to call it:
outer(5)
outer got 5
6
outer(2.5)
outer got 2.5
3.5
Predict what this call displays and which error happens. In the traceback, which function is listed first, and which one last?
outer('hi')
Answer
It displays outer’s line, and then the error happens in inner, when it tries to add the number 1 to the string 'hi': a TypeError.
outer got hi
----> 1 outer('hi')
In outer(value):
4 def outer(value):
5 print('outer got', value)
----> 6 inner(value)
In inner(value):
1 def inner(value):
----> 2 print(value + 1)
3
4 def outer(value):
5 print('outer got', value)
TypeError: can only concatenate str (not "int") to str
The traceback lists the calls in the order they were made: first the line outer('hi') in the question’s code, then the line in outer that called inner, and last the line in inner where the error happened.
The function that was running when the error happened is at the bottom, just above the error message.
Question 15 (hard): write a function#
Write a function called pyramid that takes a string char (one character) and an integer height, and displays a centered pyramid with height lines.
Line 1 has 1 copy of char, line 2 has 3, line 3 has 5, and so on; each line has enough spaces in front of it to be centered above the last line.
Start from this header (replace ... with the body):
def pyramid(char, height):
...
Examples
pyramid('*', 3)
*
***
*****
pyramid('#', 5)
#
###
#####
#######
#########
Answer
On line i (counting from 0) there are 2 * i + 1 characters and height - 1 - i spaces:
def pyramid(char, height):
for i in range(height):
print(' ' * (height - 1 - i) + char * (2 * i + 1))
Then try: pyramid('*', 3).
Another approach: write a function for one line first, then call it from a loop:
def pyramid_line(char, spaces, count):
print(' ' * spaces + char * count)
def pyramid(char, height):
for i in range(height):
pyramid_line(char, height - 1 - i, 2 * i + 1)
To find the formulas, write down the number of spaces and characters for each line of an example, and look for the pattern.
Question 16 (hard): write two functions#
Write two functions that display a grid of n by n cells:
print_border(n)displays one border line:ncopies of+---, then a final+.print_grid(n)displays the whole grid. It usesprint_border, and between two border lines it displays a line ofncopies of|(a bar and three spaces), then a final|.
Start from this header (replace ... with the body):
def print_border(n):
...
def print_grid(n):
...
Examples
print_border(3)
+---+---+---+
print_grid(2)
+---+---+
| | |
+---+---+
| | |
+---+---+
print_grid(3)
+---+---+---+
| | | |
+---+---+---+
| | | |
+---+---+---+
| | | |
+---+---+---+
Answer
A grid of n rows has a border line at the top, then, for each row, a line of bars and a border line:
def print_border(n):
print('+---' * n + '+')
def print_bars(n):
print('| ' * n + '|')
def print_grid(n):
print_border(n)
for i in range(n):
print_bars(n)
print_border(n)
Then try: print_border(3).
Another approach, without the helper function print_bars:
def print_border(n):
print('+---' * n + '+')
def print_grid(n):
for i in range(n):
print_border(n)
print('| ' * n + '|')
print_border(n)
Summary and questions by Prof. Y. Rosenthal, based on Think Python: 3rd Edition by Allen B. Downey. Text license: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International