In Python, a function is a named sequence of statements that belong together.
The syntax for a function definition is:
def name( parameters ):
statements
def name( parameters ):
statements
A header line:
begins with a keyword(def(
and ends with a colon
.
A body: consisting of one or more Python statements, each indented the same amount – 4 spaces is the Python standard – from the header line.
name:
anything you like, but must follow the rules for legal identifiers
parameters:
specify what information the function needs to do its work.
statements:
There can be any number of statements inside the function, but they have to be indented from the def.
Formal parameters
In the definition, the parameter list is more specifically known as the formal parameters. This list of names describes those things that the function will need to receive from the user of the function.
Actual parameters
When you use a function, you provide values to the formal parameters. These values, often called arguments or actual parameters, are passed to the function by the user.
Example
import turtle
def drawSquare(t, sz):
"""Make turtle t draw a square with side sz."""
for i in range(4):
t.forward(sz)
t.left(90)
alex = turtle.Turtle()
drawSquare(alex, 50)
from IPython.display import IFrame
IFrame('http://162.105.175.204:8000/runestone/static/thinkcspy/Workspace/index.html', width='100%', height=450)
docstrings
By convention, Python programmers use docstrings for the key documentation of their functions.
If the first thing after the function header is a string (mostly a triple-quoted string), it is called a docstring and gets special treatment in Python.
Try <function_name>.__doc__
in Python shell, will retrieve the docstring for the function.
This is different from comments in your code, which are completely eliminated when the program is parsed.
Function call
Defining a new function does not make the function run.
To do that we need a function call.
This is also known as a function invocation.
import turtle
def drawSquare(t, sz):
"""Make turtle t draw a square with side sz."""
for i in range(4):
t.forward(sz)
t.left(90)
alex = turtle.Turtle()
drawSquare(alex, 50)
alex.penup()
alex.goto(100,100)
alex.pendown()
drawSquare(alex,75) # Draw another square
Exercise
1. What is a function in Python?
(A) A named sequence of statements.
(B) Any sequence of statements.
(C) A mathematical expression that calculates a value.
(D) A statement of the form x = 5 + 4.
2. What is one main purpose of a function?
(A) To improve the speed of execution
(B) To help the programmer organize programs into chunks that match how they think about the solution to the problem.
(C) All Python programs must be written using functions
(D) To calculate values.
3. Which of the following is a valid function header (first line of a function definition)?
(A) def drawCircle(t):
(B) def drawCircle:
(C) drawCircle(t, sz):
(D) def drawCircle(t, sz)
4. True or false: A function can be called several times by placing a function call in the body of a loop.
(A) True
(B) False
print(abs(-5))
print(sum([1,2,3,4,5]))
print(sum(range(101)))
print(sorted([1,2,4,9,6]))
import math
print(math.pow(2, 3))
print(math.pow(7, 4))
print(max(7, 11))
print(max(4, 1, 17, 2, 12))
print(max(3 * 11, 5 ** 3, 512 - 9, 1024 ** 0))
Fruitful function
Functions that return values are sometimes called fruitful functions.
In many other languages, a chunk that doesn’t return a value is called a procedure. In Python we still call it a function, or if we want to stress it, a non-fruitful function.
Fruitful functions still allow the user to provide information (arguments).
However there is now an additional piece of data that is returned from the function.
$\require{AMScd}$ \begin{CD} @>\text{needs}>> \text{Func} @>\text{return}>> \end{CD}
Local variable
def square(x):
y = x * x
return y
toSquare = 10
result = square(toSquare)
print("The result of ", toSquare,
" squared is ", result)
Step through
from IPython.display import IFrame
IFrame("http://pythontutor.com/iframe-embed.html#code=def%20square(x%29%3A%0A%20%20%20%20y%20%3D%20x%20*%20x%0A%20%20%20%20return%20y%0A%0AtoSquare%20%3D%2010%0Aresult%20%3D%20square(toSquare%29%0Aprint(%22The%20result%20of%20%22,%20toSquare,%20%22%20squared%20is%20%22,%20result%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=false&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false", width='100%', height=300)
More about return value
Typically, functions will return values that can be printed or processed in some other way by the caller.
All Python functions return the value None unless there is an explicit return statement with a value other than None.
def square(x):
y = x * x
print(y) # Bad!
toSquare = 10
result = square(toSquare)
print("The result of ", toSquare,
" squared is ", result)
from IPython.display import IFrame
IFrame("http://pythontutor.com/iframe-embed.html#code=def%20square(x%29%3A%0A%20%20%20%20y%20%3D%20x%20*%20x%0A%20%20%20%20print(y%29%20%20%20%23%20Bad!%20%0A%0AtoSquare%20%3D%2010%0Aresult%20%3D%20square(toSquare%29%0Aprint(%22The%20result%20of%20%22,%20toSquare,%20%22%20squared%20is%20%22,%20result%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=false&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false", width='100%', height=300)
Exercise
1. What will the following function return?
def addEm(x, y, z):
print(x + y + z)
(A) Nothing (no value)
(B) The value of x + y + z
(C) The string 'x + y + z'
2. What is wrong with this func definition:
def addEm(x, y, z):
return x + y + z
print('the answer is', x + y + z)
(A) You should never use a print statement in a function definition.
(B) You should not have any statements in a function after the return statement.
(C) You must calculate the value of x+y+z before you return it.
(D) A function cannot return a number.
def square(x):
y = x * x
return y
z = square(10)
print(y)
from IPython.display import IFrame
IFrame("http://pythontutor.com/iframe-embed.html#code=def%20square(x%29%3A%0A%20%20%20%20y%20%3D%20x%20*%20x%0A%20%20%20%20return%20y%0A%0Az%20%3D%20square(10%29%0Aprint(y%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=false&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false", width='100%', height=300)
Variable lifetime
Local variable y
only exist while the function is being executed — we call this its lifetime
.
When the execution of the function terminates (returns), the local variables are destroyed.
Each call of the function creates new local variables, and their lifetimes expire when the function returns to the caller.
Global variable
y = 10
print(y)
def badsquare(x):
y = x ** power
return y
power = 2
result = badsquare(10)
print(result)
Rewrite the above code, avoid using global variables.
how variables are looked up in Python
First, Python looks at the variables that are defined as local variables in the function. We call this the local scope
.
If the variable name is not found in the local scope
, then Python looks at the global variables, or global scope
.
Assignment statements in the local function cannot change variables defined outside the function.
from IPython.display import IFrame
IFrame("http://pythontutor.com/iframe-embed.html#code=def%20powerof(x,%20p%29%3A%20%0A%20%20%20%20power%20%3D%20p%20%20%20%23%20Another%20dumb%20mistake%0A%20%20%20%20y%20%3D%20x%20**%20power%0A%20%20%20%20return%20y%0A%0Apower%20%3D%203%20%0Aresult%20%3D%20powerof(10,%202%29%0Aprint(result%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=false&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false", width='100%', height=450)
Another example
def square(x):
y = x * x
x = 0 # assign a new value to x
return y
x = 2
z = square(x)
print(z)
from IPython.display import IFrame
IFrame("http://pythontutor.com/iframe-embed.html#code=def%20square(x%29%3A%0A%20%20%20%20y%20%3D%20x%20*%20x%0A%20%20%20%20x%20%3D%200%20%20%20%20%20%20%20%23%20assign%20a%20new%20value%20to%20the%20parameter%20x%0A%20%20%20%20return%20y%0A%0Ax%20%3D%202%20%0Az%20%3D%20square(x%29%0Aprint(z%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=false&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false", width='100%', height=450)
Access global variable
using global
keyword to declare a variable as global
g = 10
def testg():
global g
g = 100
testg()
print(g)
Exercise
1. What is a variable’s scope?
(A) Its value
(B) The range of statements in the code where a variable can be accessed.
(C) Its name
2. What is a local variable?
(A) A temporary variable that is only used inside a function
(B) The same as a parameter
(C) Another name for any variable
3. Can you use the same name for a local variable as a global variable?
(A) Yes, and there is no reason not to.
(B) Yes, but it is considered bad form.
(C) No, it will cause an error.
def square(x):
runningtotal = 0
for counter in range(x):
runningtotal = runningtotal + x
return runningtotal
toSquare = 10
squareResult = square(toSquare)
print("The result of", toSquare,
"squared is", squareResult)
Accumulator
This pattern of iterating the updating of a variable is commonly referred to as the accumulator pattern. We refer to the variable as the accumulator.
Remember that the key to making it work successfully is to be sure to initialize the variable before you start the iteration.
Once inside the iteration, it is required that you update the accumulator.
from IPython.display import IFrame
IFrame("http://pythontutor.com/iframe-embed.html#code=def%20square(x%29%3A%0A%20%20%20%20runningtotal%20%3D%200%0A%20%20%20%20for%20counter%20in%20range(x%29%3A%0A%20%20%20%20%20%20%20%20runningtotal%20%3D%20runningtotal%20%2B%20x%0A%0A%20%20%20%20return%20runningtotal%0A%0AtoSquare%20%3D%2010%0AsquareResult%20%3D%20square(toSquare%29%0Aprint(%22The%20result%20of%22,%20toSquare,%20%22squared%20is%22,%20squareResult%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=false&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false", width='100%', height=400)
It is important to understand that each of the functions we write can be used and called from other functions we write.
This is one of the most important ways that computer scientists take a large problem and break it down into a group of smaller problems.
This process of breaking a problem into smaller subproblems is called functional decomposition.
def square(x):
y = x * x
return y
def sum_of_squares(x, y, z):
a = square(x)
b = square(y)
c = square(z)
return a + b + c
a,b,c = -5,2,10
result = sum_of_squares(a, b, c)
print(result)
from IPython.display import IFrame
IFrame("http://pythontutor.com/iframe-embed.html#code=def%20square(x%29%3A%0A%20%20%20%20y%20%3D%20x%20*%20x%0A%20%20%20%20return%20y%0A%20%20%20%20%0Adef%20sum_of_squares(x,%20y,%20z%29%3A%20%0A%20%20%20%20a%20%3D%20square(x%29%0A%20%20%20%20b%20%3D%20square(y%29%0A%20%20%20%20c%20%3D%20square(z%29%0A%20%20%20%20%0A%20%20%20%20return%20a%20%2B%20b%20%2B%20c%0A%0Aa%20%3D%20-5%0Ab%20%3D%202%20%0Ac%20%3D%2010%0Aresult%20%3D%20sum_of_squares(a,%20b,%20c%29%0Aprint(result%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=false&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false", width='100%', height=500)
The above example illustrates many very important Python concepts, including:
local variables
global variables
parameter passing
import turtle
def drawRectangle(t, w, h):
"""Get turtle t to draw a rectangle of width w and height h."""
for i in range(2):
t.forward(w)
t.left(90)
t.forward(h)
t.left(90)
def drawSquare(tx, sz):
drawRectangle(tx, sz, sz)
tess = turtle.Turtle() # create tess
drawSquare(tess, 50)
This example illustrates an important computer science problem solving technique called generalization.
Why bother to create new functions
Creating a new function gives you an opportunity to name a group of statements.
Creating a new function can make a program smaller by eliminating repetitive code.
Exercise: Drawing a circle
import turtle
def drawPolygon(t, sideLength, numSides):
# fill your code here
def drawCircle(anyTurtle, radius):
# fill your code here
wheel = turtle.Turtle()
drawCircle(wheel, 40)
from IPython.display import IFrame
IFrame('http://162.105.175.204:8000/runestone/static/thinkcspy/Workspace/index.html', width='100%', height=450)
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.
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.
Exercise:
Analyze the execution flow of the following code.
def pow(b, p):
y = b ** p
return y
def square(x):
a = pow(x, 2)
return a
n = 5
result = square(n)
print(result)
from IPython.display import IFrame
IFrame("http://pythontutor.com/iframe-embed.html#code=def%20pow(b,%20p%29%3A%0A%20%20%20%20y%20%3D%20b%20**%20p%0A%20%20%20%20return%20y%0A%0Adef%20square(x%29%3A%0A%20%20%20%20a%20%3D%20pow(x,%202%29%0A%20%20%20%20return%20a%0A%0An%20%3D%205%0Aresult%20%3D%20square(n%29%0Aprint(result%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=false&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false", width='100%', height=400)
Before the Python interpreter executes your program, it defines a few special variables.
One of those variables is called __name__
and it is automatically set to the string value "__main__
" when the program is being executed by itself in a standalone fashion.
If the program is being imported by another program, the __name__
variable is set to the name of that module.
Usually we would not want to execute the main function if imported by another program.
def squareit(n):
return n * n
def cubeit(n):
return n*n*n
def main():
anum = int(input("Please enter a number"))
print(squareit(anum))
print(cubeit(anum))
if __name__ == "__main__":
main()
Exercise
Write a fruitful function sumTo(n)
that returns the sum of all integer numbers up to and including n
. So sumTo(10)
would be 1+2+3...+10
which would return the value 55
.
def sumTo(n):
# fill the code here
return result
# Now lets see how well this works
t = sumTo(0)
print("The sum from 1 to 0 is",t)
t = sumTo(10)
print("The sum from 1 to 10 is",t)
t = sumTo(5)
print("The sum from 1 to 5 is",t)
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.
Example: distance between two points
The coordinates of $(x_1, y_1)$ and $(x_2, y_2)$
$\text{distance} = \sqrt{(x_2-x_1)^2+(y_2-y_1)^2}$
Example: step 1
def distance(x1, y1, x2, y2):
return 0.0
Run and test it.
def distance(x1, y1, x2, y2):
return 0.0
print(distance(1, 2, 4, 6))
Example: step 2
def distance(x1, y1, x2, y2):
dx = x2 - x1
dy = y2 - y1
return 0.0
Example: step 3
def distance(x1, y1, x2, y2):
dx = x2 - x1
dy = y2 - y1
dsquared = dx**2 + dy**2
return 0.0
Example: step 4
def distance(x1, y1, x2, y2):
dx = x2 - x1
dy = y2 - y1
dsquared = dx**2 + dy**2
result = dsquared**0.5
return result
print(distance(1, 2, 4, 6))
About the incremental development process
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.
Use temporary variables to hold intermediate values so that you can easily inspect and check them.
Once the program is working, you might want to consolidate multiple statements into compound expressions, but only do this if it does not make the program more difficult to read.
Functions can be called from within another. This ability to build functions by using other functions is called composition.
Example
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.
radius = distance(xc, yc, xp, yp)
def distance(x1, y1, x2, y2):
dx = x2 - x1
dy = y2 - y1
dsquared = dx**2 + dy**2
return dsquared**0.5
def area(xc, yc, xp, yp):
radius = distance(xc, yc, xp, yp)
return 3.14159 * radius**2
print(area(0,0,1,1))
print
¶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.
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.
It is a common best practice in software development to include automatic unit testing of source code.
Unit testing provides a way to automatically verify that individual pieces of code, such as functions, are working properly.
Unit testing also forces the programmer to think about the different cases that the function needs to handle.
test suite
Extra code in your program which is there because it makes debugging or testing easier is called scaffolding
.
A collection of tests for some code is called its test suite
.
Unit testing example using assert
def absolute_value(n):
""" Compute the absolute value of n """
return abs(n)
def absolute_value(n): # Buggy version
""" Compute the absolute value of n """
if n < 0:
return 1
elif n > 0:
return n
def test_suite():
""" Run the suite of tests for code in this module (this file).
"""
assert(absolute_value(17) == 17)
assert(absolute_value(-17) == 17)
assert(absolute_value(0) == 0)
assert(absolute_value(3.14) == 3.14)
assert(absolute_value(-3.14) == 3.14)
test_suite() # Here is the call to run the tests
chatterbox function
A function which interacts with the user (using input or print) when it should not. Silent functions that just convert their input arguments into their output results are usually the most useful ones.
composition (of functions)
Calling one function from within the body of another, or using the return value of one function as an argument to the call of another.
dead code
Part of a program that can never be executed, often because it appears after a return statement.
fruitful function
A function that yields a return value instead of None.
incremental development
A program development plan intended to simplify debugging by adding and testing only a small amount of code at a time.
None
A special Python value. One use in Python is that it is returned by functions that do not execute a return statement with a return argument.
return value
The value provided as the result of a function call.
scaffolding
Code that is used during program development to assist with development and debugging. The unit test code that we added in this chapter are examples of scaffolding.
temporary variable
A variable used to store an intermediate value in a complex calculation.