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,listobjectsThe 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
functionfor user-defined functions orbuiltin_function_or_methodfor 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
defis the required Python keywordfunction_name is the name/identifier of the function
Function parameters/arguments follow the function name within
()The
defline 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
xandycan 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
NoneThis 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
returnstatementText displayed by
printfunctions are not ouput
The
returnstatement: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
Noneand 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
returnstatement 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
showwill behave exactly likeprint
show("x", "y", sep=' --> ')
x --> y
The assignment operator creates a reference to the
printfunction 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
tupleorlistAnother 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
tupleobject
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 atuplewith specified parameter nameargsscalebecomes 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
printuses tuple packing and keyword-only default parameters
help(print)
Argument dictionary packing packs arbitrary number of inputs into a single
dictionaryobject
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 singledictobject with the specified parameter namekwargsAll 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
lambdaexpression
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
lambdaexpression
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
globalwhich 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
totandnare 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}')
totinside and outside the function will be treated differently and have different valuesThe
totoutside the function isglobalThe
totinside the function islocal
To use a
localvalue outside the function, declare itglobalIf 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
returnstatement 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
replacemethod 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
returnfor error handlingWrite a function to find the factors of a positive integer
Use an early
returnto avoid exceptionsreturnwith no output is similar tobreakinside 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
returnfor error handlingWrite a function to find the factors of a positive integer
Use an early
returnto avoid exceptionsreturnwith no output is similar tobreakinside 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)