Functions#
Consider a mathematical function like \(z = 3x + y\) evaluated when \(x = 3\) and \(y = 6\)
\(x\) and \(y\) are input into the function as 3 and 6 and \(z\) is output as 15

Functions in computer programming can be thought of as a generalization of mathematical functions
Finite number of inputs (or parameters or arguments) are provided
Functions can have 0 inputs too
Result is an output
E.g.,
int
,float
,list
objectsThe output can also be
None
(e.g.,print()
function)
Some functions modify an input object if it is mutable (e.g.,
list
,dict
)

Functions are a control flow tool that:
Encapsulate a block of code that can be called on to perform some task repeatedly
Modularize the program
Help in dividing and conquering a problem to be solved
Aid in identifying and fixing bugs
Functions in Python:
Built-in functions
print
,input
,len
, etc.
User-defined functions
Functions created by users
Functions are objects too in Python
Function name is the identifier
They are of type
function
for user-defined functions orbuiltin_function_or_method
for built-in functions
A function is a compound statement
def function_name(parameter1, parameter2, ..., parameterN):
'''
Descriptive string which is accessed by help function
'''
# body of function
return output_parameters # optional
def
is the required Python keywordfunction_name is the name/identifier of the function
Function parameters/arguments follow the function name within
()
The
def
line is ended with a:
Function body - indented code that will be executed on calling the function
An optional help string can be included in the beginning
https://docs.python.org/3/tutorial/controlflow.html#defining-functions https://docs.python.org/3/reference/compound_stmts.html?highlight=function#function-definitions
Functions must be called to do anything
To call a function, simply use its name and pass in necessary arguments
E.g
len("Hello")
calls the len function passing in the argument"Hello"

Functions must be defined before they are called
Examples#
def mathFun(x, y):
z = 3 * x + y
return z
a, b = 3, 6
c = mathFun(a, b)
print(f"{c} = 3*{a} + {b}")
15 = 3*3 + 6
def math_function(x, y):
z = 3*x + y
return z
x, y = 3, 6
z = math_function(x, y)
print(z)
15
The variable names
x
andy
can be reused outside the function definition
Write a function that defines the determinant and use it in a program
Include help text
def determinant(a, b, c, d):
'''
Function to find the determinant of a 2x2 matrix
[a, b
c, d]
'''
det = a*d - b*c
print(det)
determinant(1, 2, 3, 4)
-2
help(determinant)
Help on function determinant in module __main__:
determinant(a, b, c, d)
Function to find the determinant of a 2x2 matrix
[a, b
c, d]
type(determinant)
function
The output of a function is captured by a variable with an assignment operation with the function call on the RHS
d = determinant(1, 3, 5, 7)
type(d)
-8
NoneType
The output of this function is
None
This is to say the function provides no output
Functions with no output are referred to as void functions in C/C++
A function can be redefined with a different set of parameters and body (just like any other variable)
def determinant():
pass # Do nothing
determinant() # No output
This function now has neither an input nor an output
The return
Statement#
An output specifically requires the
return
statementText displayed by
print
functions are not ouput
The
return
statement:Signifies the end of the function body
The variable(s)/expression next to the return keyword will be the output of the function
If not used or no variable follows the return keyword, the output is
None
and the function is a void functionExpressions and statements following this in the function’s body will be ignored
def determinant(a, b, c, d):
det = a*d - b*c
return det
determinant(1, 3, 4, 2)
-10
To capture the returned output, use the assignment operator
d = determinant(1, 3, 4, 2)
d
-10
def determinant(a, b, c, d):
det = a*d - b*c
return det
det *= 2
determinant(1, 3, 4, 2)
-10
The code following the
return
statement is ignored, even if it is syntactically correct
Function Objects#
Functions can be copied
Allows for creating aliases for function names
Helpful when you wish to have a shorthand for a commonly used function
show = print
a = (1 + 5**0.5)/2
show(f"Golden Ratio = {a:0.6f}")
Golden Ratio = 1.618034
The function
show
will behave exactly likeprint
show("x", "y", sep=' --> ')
x --> y
The assignment operator creates a reference to the
print
function object
# Both function objects have the same id
show is print
True
Recursive Functions#
A recursive function is a function that calls itself
Word of caution: recursion, while elegant, may not be the most effective solution in terms of computational time and memory usage
Ex: factorial of a number using recursion
def fact(n):
if n == 1:
return 1
# Recursion
return n*fact(n-1)
fact(6)
720
%%html
<iframe width="1200" height="600" frameborder="0" src="https://pythontutor.com/iframe-embed.html#code=def%20fact%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20%0A%20%20%20%20%23%20Recursion%0A%20%20%20%20return%20n*fact%28n-1%29%20%20%20%20%0A%20%20%20%20%0Afact%285%29&codeDivHeight=400&codeDivWidth=350&cumulative=true&curInstr=0&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe>
Argument Passing#
Function arguments can be passed using:
Positional arguments
Keyword arguments
Mix of positional and keyword arguments
Keyword arguments must follow positional arguments
Keyword-only or positional-only arguments
Variable length arguments
Argument tuple packing
Argument dictionary packing
Default parameters
Created only once per function
Care should be taken when the datatype is mutable
Positional Arguments#
Arguments are passed in the same order as the parameter list
The number of arguments should match the number of parameters
This is the common way of passing arguments
# Function that emulates the range object
def myRangeFun(start, stop, step):
'''My range implementation'''
# Accumulator
seq = []
# Loop
nextVal = start
while True:
if nextVal >= stop:
break
seq.append(nextVal)
nextVal += step
return seq
print(myRangeFun(4, 10, 1))
print(list(range(4, 10)))
[4, 5, 6, 7, 8, 9]
[4, 5, 6, 7, 8, 9]
What is the expected and actual output?
myRangeFun(10, 0, -2)
[]
This implementation of range is not handling decreasing sequences
Keyword Arguments#
The keywords (parameter names) are used to pass the arguments
Arguments can be passed in any order, but their number should still match the number of parameters
# Function that emulates the range object
def myRangeFun(start, stop, step):
'''My range implementation'''
# Accumulator
seq = []
# Loop
nextVal = start
while True:
if nextVal >= stop:
break
seq.append(nextVal)
nextVal += step
return seq
myRangeFun(step = 2, stop = 10, start = 0)
[0, 2, 4, 6, 8]
Positional and Keyword Arguments#
Positional and keyword arguments can be combined
e.g.
print("Hello", end = ' ')
However, positional arguments cannot follow keyword arguments
Will result in an exception
myRangeFun(start = 2, 10, step = 2)
Cell In[21], line 1
myRangeFun(start = 2, 10, step = 2)
^
SyntaxError: positional argument follows keyword argument
# Correct
myRangeFun(2, step = 2, stop = 10)
Default Parameters#
Functions can have default parameters
The default is used when no input is specified
# Function that emulates the range object
def myRangeFun(start = 0, stop = 10, step = 2):
'''My range implementation'''
# Accumulator
seq = []
# Loop
nextVal = start
while True:
if nextVal >= stop:
break
seq.append(nextVal)
nextVal += step
return seq
myRangeFun()
Default parameters can be overridden when the function is called
e.g.
print("Hello", sep = '/n')
will overwrite default for sep but still use default for end
myRangeFun(2)
myRangeFun(2, stop = 4)
Non-default parameters cannot follow default parameters
E.g., this is not valid:
def myRangeFun(start=0, stop, step=2):
pass
Example#
Function to average three numbers
def average(a, b, c):
return (a+b+c)/ 3
Consider using this function with positional only, keyword only, or positional + keyword arguments
average(1.2, 0, -1.2) # positional
average(c = -1.2, b = 0, a = 1.2) # keyword
average(1.2, c = -1.2, b = 0) # mixed
How can you modify the function to include default parameters?
# With default parameters
def average(a=0, b=0, c=0):
return (a+b+c)/3
average() # Positional
average(c = 3) # Keyword
average(1, c = 3) # Mixed
Variable Length Arguments#
What if you do not know a priori how many parameters the function needs?
One option is to use a
tuple
orlist
Another option is to use the built-in functionality of:
Argument tuple packing
Argument dictionary packing
Argument tuple packing packs arbitrary number of inputs into a single
tuple
object
def average(*args, scale=1):
n = len(args)
tot = sum(args)
return tot/n*scale
The asterisk
*
tells Python that the arguments for this function should be packed into atuple
with specified parameter nameargs
scale
becomes a keyword-only argument with a default value of 1Must be passed with the keyword
average(1, 2, 3, 4, 5)
The built in function
print
uses tuple packing and keyword-only default parameters
help(print)
Argument dictionary packing packs arbitrary number of inputs into a single
dictionary
object
def average(**kwargs):
for key, val in kwargs.items():
print(f"{key}->{val}", end=', ')
print()
n = len(kwargs)
total =sum(kwargs.values())
return total/n
Use of two asterisks
**
indicates that the arguments to this functions should be packed into a singledict
object with the specified parameter namekwargs
All inputs should be keyword arguments
average(a = 1 , b = 3, c = 2.4, d = 5.3)
average(1, 2, c = 3, d = 5) # Error - no keywords for first two inputs
More Argument Passing#
Other possibilities not discussed:
Keyword-only arguments
Positional-only arguments
Positional arguments with tuple and/or dictionary packing
Tuple and dictionary unpacking
…
Lambda Functions#
An anonymous inline function with a single expression
Syntax:
lambda arguments: expression
adder = lambda x, y: x + y
adder(1, 2)
(lambda x, y, z: x*y*z)(1, 2, 3)
Sorting a list of tuples alphabetically with a
lambda
expression
pairs = [(1, 'January'), (2, 'February'), (3, 'March'), (4, 'April')]
pairs.sort(key = lambda pair: pair[1])
pairs
Sorting a list of tuples by string size with a
lambda
expression
pairs = [(1, 'January'), (2, 'February'), (3, 'March'), (4, 'April')]
pairs.sort(key = lambda pair: len(pair[1]))
pairs
Local vs. Global Variables#
A function has its own memory
Variables defined within a function are local, only existing within the function
Once a function completes, the local variables are forgotten
They cannot be accessed by the general program
Variables can be declared as
global
which makes them available outside of the function
def average(*args, scale=1):
n = len(args)
tot = sum(args)
return tot/n*scale
a = average(1, 3, 5, 7)
print(tot)
The variables
tot
andn
are local to the functionaverage
tot = 0
def average(*args, scale=1):
n = len(args)
tot = sum(args)
print(f'Inside the function - {tot}')
return tot/n*scale
a = average(1, 3, 5, 7)
print(f'Outside the function - {tot}')
tot
inside and outside the function will be treated differently and have different valuesThe
tot
outside the function isglobal
The
tot
inside the function islocal
To use a
local
value outside the function, declare itglobal
If a variable with the same name exists outside the function, it is modified inside the function
Otherwise, the variable will be made available outside the function
def average(*args, scale=1):
global tot
n = len(args)
tot = sum(args)
print(f'Inside the function - {tot}')
return tot/n*scale
a = average(1, 3, 5, 7)
print(f'Outside the function - {tot}')
Examples#
Conditional return
We can return values conditionally inside of our function
Note that only one
return
statement can be executedFunction immediately terminates once the first return is executed
Write a function that determines if a word or a sentence is a palindrome
def is_palindrome(word):
word = word.lower().replace(' ', '')
if word == word[::-1]:
return True
return False
replace
method here removes spaces by replacing them with empty strings''
test_word = 'tenet'
is_palindrome(test_word)
is_palindrome('never odd or even')
is_palindrome('3043')
Multiple outputs
Write a function that outputs both the sum and product of a set of numbers
Use variable length for the inputs
You can save the outputs to multiple individual variables or one tuple
def sum_prod(*nums):
s, p = 0, 1
for i in nums:
s += i
p *= i
return s, p
output = sum_prod(1, 2, 3, 4)
print(output)
output1, output2 = sum_prod(1, 2, 3, 4)
print(output1)
print(output2)
Using
return
for error handlingWrite a function to find the factors of a positive integer
Use an early
return
to avoid exceptionsreturn
with no output is similar tobreak
inside of a loop
def factors(num):
if not(type(num) == int and num > 0):
print("That wasn't a positive integer!")
return
factors = []
for i in range(2, num//2 + 1):
if num%i == 0:
factors.append(i)
return factors
factors(2450)
Using
return
for error handlingWrite a function to find the factors of a positive integer
Use an early
return
to avoid exceptionsreturn
with no output is similar tobreak
inside of a loop
def factors(num):
if not(type(num) == int and num > 0):
print("That wasn't a positive integer!")
return
factors = []
for i in range(2, num//2 + 1):
if num%i == 0:
factors.append(i)
return factors
factors(2450)
Finding primes
Write a function to determine whether or not a number is prime. The function should take an integer input and return either True or False.
Use this function inside of a loop to create a list of all prime numbers from 1 to 100
def is_prime(num):
if not(type(num) == int and num > 0):
print("That wasn't a positive integer!")
return
if num == 1:
return False
for i in range(2, int(num**0.5) + 1):
if num%i == 0:
return False
return True
is_prime(13)
is_prime(13139480598340259)
primes = []
for i in range(1, 101):
if is_prime(i):
primes.append(i)
print(primes)