Jupyter Snippet P4M 06

Jupyter Snippet P4M 06

All of these python notebooks are available at https://gitlab.erc.monash.edu.au/andrease/Python4Maths.git


Functions can represent mathematical functions. More importantly, in programmming functions are a mechansim to allow code to be re-used so that complex programs can be built up out of simpler parts.

Important: Starting to write a python program by just writing a few lines of code and testing as you go is great – but a common beginner mistake is to keep doing this. You do not want to have a program that consists of 20,000 lines in one long file/notebook. Think of functions like paragraphs in writing English. Whenever you start a new idea, start a new function. This makes your code much more readable, easier to debug and ultimately to re-use parts of the code in ways that may not have been anticipated when initially writing the code.

This is the basic syntax of a function

def funcname(arg1, arg2,... argN):
    ''' Document String'''
    return value```

Read the above syntax as, A function by name "funcname" is defined, which accepts arguements "arg1,arg2,....argN". The function is documented and it is '''Document String'''. The function after executing the statements returns a "value".

Return values are optional (by default every function returns `None` (a special object that is equivalent to `False` ) if no return statement is executed

print("Hello Jack.")
print("Jack, how are you?")
Hello Jack.
Jack, how are you?

Instead of writing the above two statements every single time it can be replaced by defining a function which would do the job in just one line.

Defining a function firstfunc().

def firstfunc():
    print("Hello Jack.")
    print("Jack, how are you?")
firstfunc() # execute the function
Hello Jack.
Jack, how are you?

firstfunc() just prints the message every time to a single person. We can make our function firstfunc() to accept arguments which will store the name and then prints its message to that name. To do so, add a argument within the function as shown.

def firstfunc(username):
    print("Hello %s." % username)
    print(username + ',' ,"how are you?")
name1 = 'Sally' # or use input('Please enter your name : ')

So we pass this variable to the function firstfunc() as the variable username because that is the variable that is defined for this function. i.e name1 is passed as username.

Hello Sally.
Sally, how are you?

Return Statement

When the function results in some value and that value has to be stored in a variable or needs to be sent back or returned for further operation to the main algorithm, a return statement is used.

def times(x,y):
    z = x*y
    return z
    z = 17 # this statement is never executed

The above defined times( ) function accepts two arguements and return the variable z which contains the result of the product of the two arguements

c = times(4,5)

The z value is stored in variable c and can be used for further operations.

Instead of declaring another variable the entire statement itself can be used in the return statement as shown.

def times(x,y):
    '''This multiplies the two input arguments'''
    return x*y
c = times(4,5)

Since the times() is now defined, we can document it as shown above. This document is returned whenever times() function is called under help() function.

Help on function times in module __main__:

times(x, y)
    This multiplies the two input arguments

Multiple variable can also be returned as a tuple. However this tends not to be very readable when returning many value, and can easily introduce errors when the order of return values is interpreted incorrectly.

eglist = [10,50,30,12,6,8,100]
def egfunc(eglist):
    highest = max(eglist)
    lowest = min(eglist)
    first = eglist[0]
    last = eglist[-1]
    return highest,lowest,first,last

If the function is just called without any variable for it to be assigned to, the result is returned inside a tuple. But if the variables are mentioned then the result is assigned to the variable in a particular order which is declared in the return statement.

(100, 6, 10, 100)
a,b,c,d = egfunc(eglist)
print(' a =',a,' b =',b,' c =',c,' d =',d)
 a = 100  b = 6  c = 10  d = 100

Default arguments

When an argument of a function is common in majority of the cases this can be specified with a default value. This is also called an implicit argument.

def implicitadd(x,y=3,z=0):
    print("%d + %d + %d = %d"%(x,y,z,x+y+z))
    return x+y+z

implicitadd( ) is a function accepts up to three arguments but most of the times the first argument needs to be added just by 3. Hence the second argument is assigned the value 3 and the third argument is zero. Here the last two arguments are default arguments.

Now if the second argument is not defined when calling the implicitadd( ) function then it considered as 3.

4 + 3 + 0 = 7


However we can call the same function with two or three arguments. A useful feature is to explicitly name the argument values being passed into the function. This gives great flexibility in how to call a function with optional arguments. All off the following are valid:

4 + 4 + 0 = 8
4 + 5 + 6 = 15
4 + 3 + 7 = 14
2 + 1 + 9 = 12
1 + 3 + 0 = 4


Any number of arguments

If the number of arguments that is to be accepted by a function is not known then a asterisk symbol is used before the name of the argument to hold the remainder of the arguments. The following function requires at least one argument but can have many more.

def add_n(first,*args):
    "return the sum of one or more numbers"
    reslist = [first] + [value for value in args]
    return sum(reslist)

The above function defines a list of all of the arguments, prints the list and returns the sum of all of the arguments.

[1, 2, 3, 4, 5]



Arbitrary numbers of named arguments can also be accepted using **. When the function is called all of the additional named arguments are provided in a dictionary

def namedArgs(**names):
    'print the named arguments'
    # names is a dictionary of keyword : value
    print("  ".join(name+"="+str(value) 
                    for name,value in names.items()))

x=12  animal=mouse  z=(1+2j)

Global and Local Variables

Whatever variable is declared inside a function is local variable and outside the function in global variable.

eg1 = [1,2,3,4,5]

In the below function we are appending a element to the declared list inside the function. eg2 variable declared inside the function is a local variable.

def egfunc1():
    def thirdfunc():
        print("Inside thirdfunc x =", x) 
    print("Outside x =", x)
print("Global x =",x)
Inside thirdfunc x = 2
Outside x = 1
Global x = 12

If a global variable is defined as shown in the example below then that variable can be called from anywhere. Global values should be used sparingly as they make functions harder to re-use.

eg3 = [1,2,3,4,5]
def egfunc1():
    x = 1.0 # local variable for egfunc1
    def thirdfunc():
        global x # globally defined variable 
        x = 2.0
        print("Inside thirdfunc x =", x) 
    print("Outside x =", x)
print("Globally defined x =",x)
Inside thirdfunc x = 2.0
Outside x = 1.0
Globally defined x = 2.0

Lambda Functions

These are small functions which are not defined with any name and carry a single expression whose result is returned. Lambda functions comes very handy when operating with lists. These function are defined by the keyword lambda followed by the variables, a colon and the expression.

z = lambda x: x * x

Composing functions

Lambda functions can also be used to compose functions

def double(x):
    return 2*x
def square(x):
    return x*x
def f_of_g(f,g):
    "Compose two functions of a single variable"
    return lambda x: f(g(x))
doublesquare= f_of_g(double,square)
print("doublesquare is a",type(doublesquare))
doublesquare is a <class 'function'>


Functions are objects

Functions are a type of “value” that can be assigned to variables, stored in lists and so on.

def f(x):
    return 2*x**2 +3*x-5
g = f
print("g(3.6) =",g(3.6) )

for func in [f,square, double, doublesquare]:
    print("evaluating at 2.0 yields:", func(2.0) )
g(3.6) = 31.72
evaluating at 2.0 yields: 9.0
evaluating at 2.0 yields: 4.0
evaluating at 2.0 yields: 4.0
evaluating at 2.0 yields: 8.0