References
The WC3 Schools reference states:
"A function is a block of code which only runs when it is called."
"You can pass data, known as parameters, into a function."
"A function can return data as a result."
In the next example we will implement a "bubble sort" to arrange elements in a list to be ordered from smallest to largest. In Ex 1.6.1 we will define a function named bsort(). The function will sort a list of items. These items may be integers, floats, or strings.
The definition of the function begins with def bsort(): and the statements in that function are an indented block of code ending with return.
The argument(s) of the function are found within the parentheses. In Ex1.6.1 that will be a list A.
The block of code (lines 3-8) which defines the functions begins with finding the length (number of elements) of the list n . (remember the index of a list starts at 0 and ranges to n-1.
Here is a reference to the bubble sort algorithm The algorithm requires two passes through the list to compare adjacent elements. If those elements are not in proper sorted order, the position of the elements in the list are swapped. This is accomplished by a nested loop structure. Line 7 compares the order of the pair of elements, and the statement after the colon implements the swap.Python gives us a nifty way to do that in a single statement A[j], A[i] = A[i], A[j]. In fact, if you did this with two consecutive statemtns you would do a swap and then undo the swap. If you do this with two statements it is necessary to introduce holding variables A1=A[i] then A2=A[j] then swap values. The construct in line 7 avoids this extra step.
Note that when the function is first called, the argument A is assigned X, while in the second call it is assigned animals. Since the condition for ordering is the same for lists of strings, integers, or floats, we can use the same function to sort these different elements.
Notice that in this case the argument of the function is altered by the function. When A is returned, it reassigns the reorderd A to the variable X (or in the second call to animals)
# Ex1.6.1
def bsort(A):
n = len(A)
for i in range(0,n-1): # Outer Loop
for j in range(0,n-1): # Inner Loop
if A[j]> A[i]: A[j], A[i] = A[i], A[j]
return
X = [3,1,7,8,5]
print("the original list=", X)
bsort(X)
print("the sorted list=", X,'\n')
animals=["dog","cat","mouse","elephant","tiger","lion"]
print("the original list=", animals)
bsort(animals)
print("the sorted list=", animals,'\n')
flt_nos=[17.6,-3.2,47.99,-200,342,.001,0,46.3]
print("the original list=", flt_nos)
bsort(flt_nos)
print("the sorted list=", flt_nos)
the original list= [3, 1, 7, 8, 5] the sorted list= [1, 3, 7, 8, 5] the original list= ['dog', 'cat', 'mouse', 'elephant', 'tiger', 'lion'] the sorted list= ['cat', 'dog', 'elephant', 'mouse', 'tiger', 'lion'] the original list= [17.6, -3.2, 47.99, -200, 342, 0.001, 0, 46.3] the sorted list= [-200, -3.2, 0, 0.001, 17.6, 47.99, 342, 46.3]
While this is an interesting exercise, note that Python provides a predefined method to sort a list. A method is a function defined to work on a particular class of objects. In order to sort a list A, we simply write
We are asked to write a Python function to calculate the volume of a general polyhedron including tetrahedron (4 sides), cube (6 sides), octahedron (8 sides), dodecahedron (12 sides), and an icosahedron (20 sides). We will identify each polyhedron by its number of faces and the length of a side.
The function is called within the print statement (line 12). When you call the function with a specified return value, that call will be the return value. That is polyhed(8,1) equals 0.47140452079103173 which is printed. The for loop (lines 11-12) iterates over the values found in the tuple (4,6,8,12,20).
# Ex1.6.2
import math as mth
def polyhed(n,a): #n= number of faces, a=length of a side
if n==4: V=mth.sqrt(2.)*a**3/12
elif n==6: V=a**3
elif n==8: V=(mth.sqrt(2)*a**3)/3
elif n==12: V=((15+7*mth.sqrt(5))*a**3)/4
elif n==20: V=(5*(3+mth.sqrt(5.))*a**3)/12
return V
a=1.
for n in (4,6,8,12,20):
print("the volume of polyhedron with ",n," faces and a side length of ",a, " =",polyhed(n,a))
the volume of polyhedron with 4 faces and a side length of 1.0 = 0.11785113019775793 the volume of polyhedron with 6 faces and a side length of 1.0 = 1.0 the volume of polyhedron with 8 faces and a side length of 1.0 = 0.47140452079103173 the volume of polyhedron with 12 faces and a side length of 1.0 = 7.663118960624632 the volume of polyhedron with 20 faces and a side length of 1.0 = 2.1816949906249126
Recursion is a process whereby a function calls itself. In the following example, we define a factorial(n) in which the name of the function is factorial, and it has a single argument n. If n==1, we return a value of 1. For any integer (e.g. 5)
The diagram above illustrates the flow within the recursion process. The original call to factorial passes 5 as an argument to the function. The function then executes a call to itself to compute 4!, then 3! and finally 2! At this point we begin a process of returns. 2! returns 2 to the factorial(3). At that level the product of 3 and 2 is computed and returned to 4!, which in turn passes the product of 4 times 6 to the original call which returns 5 times 6 to the main program.
There is a limit to the number of recursions in Python. that number is 1000.
# Ex1.6.3
def factorial(n):
if n == 1:
return 1
else:
return (n * factorial(n-1))
N=5
print(factorial(N))
120
A Lambda function is a small(one statement) function that has no name. In the example that follows the lambda function has two arguments a and b which are string variables. the function concatenates these to form a full name. Two things to notice: First, there is no def to define the function. The function begins with the word lambda followed by any number of arguments terminated by : The single statement that combines the parameters to form the result is given by the statement following the semicolon. Since there is no return or even function name, any variable can be defined to hold the result and this is set equal to the lambda expression, in this case name which is equal to lambda a,b: a+ " " + b. NOTE: there are no () in the definition of the lambda function.
# Ex1.6.4
name=lambda a,b: a+ " " + b
print(name("john","Doe"))
john Doe
In Python, we can pass any object to a function. In all of the examples we have considered thus far, the arguments have been numbers or strings. In this example we will pass a a lambda function to a Python method.Remember a method is a function defined over a class of objects.
In this example we will define a list of animals. We will do four sorts to that list:
Reference: https://www.w3schools.com/python/ref_list_sort.asp
alpha sort
Executed in line 3 just like we did before.inverse alpha sort
Executed in line 6. Note the argument reverse=True.sort by string length
Executed in 9. Here the argument becomes key= the length of the list animals. In order to find the length of the list animals we need to pass a function as an argument in the .sort() method. In Python, functions are objects, and the argument of a function is also an object. Let's try to pass a lambda function within .sort(). In line 9 we write animals.sort(key=lambda item:(len(item))). Here item is an element in the list animals.The lambda function will return a value equal to the length of item. It sets that equal to key. Just as an alpha sort will iterate through items and sort by alpha, this time it will iterate thru items and sort by the length of the item.sort by 1) string length then 2) by alpha
In the third sort, we used the variable item to stand for some characteristic of the member of the list. There is nothing special about the name item. We could use anything. In this sort I have used x: len(x). You could use animal: len(animal). However, DO NOT USE animals as this name is already used for the list name.
# Ex1.6.5
animals=["dog","cat","elephant","horse","mouse","ant","halibut","ardvark"]
animals.sort()
print("animals sorted by alpha\n",animals,"\n")
animals=["dog","cat","elephant","horse","mouse","ant","halibut","ardvark"]
animals.sort(reverse=True)
print("animals sorted by inverse alpha\n",animals,"\n")
animals=["dog","cat","elephant","horse","mouse","ant","halibut","ardvark"]
animals.sort(key=lambda item:(len(item)))
print("animals sorted by string length\n",animals,"\n")
animals=["dog","cat","elephant","horse","mouse","ant","halibut","ardvark"]
animals.sort(key=lambda x: (len(x),x))
print("animals sorted by string length then alpha\n",animals)
animals sorted by alpha ['ant', 'ardvark', 'cat', 'dog', 'elephant', 'halibut', 'horse', 'mouse'] animals sorted by inverse alpha ['mouse', 'horse', 'halibut', 'elephant', 'dog', 'cat', 'ardvark', 'ant'] animals sorted by string length ['dog', 'cat', 'ant', 'horse', 'mouse', 'halibut', 'ardvark', 'elephant'] animals sorted by string length then alpha ['ant', 'cat', 'dog', 'horse', 'mouse', 'ardvark', 'halibut', 'elephant']
Reference https://www.freecodecamp.org/news/args-and-kwargs-in-python/
Suppose we wanted to write a function to add two numbers.
Now suppose we want to add three numbers. Do we have to write another function? The answer is no. consider the function in Ex1.6.7 shown below. When you use the astirix before an argument, you specify the argument as a tuple with however many elements you wish. Clearly, we can mix integers and floats in addition.
You will often see this written as args, however, in the function you can use any variable name for args such as numbers in the example below
# Ex1.6.6
def add(*numbers):
sum=0
for num in numbers:
sum+=num
return sum
print(add(2,3))
print(add(1,2,3))
print(add(12.5,4,6,7.2,100))
5 6 129.7
Try removing the * from the argument of the function defined on line 2. You will get an error.
reference:
https://www.w3schools.com/python/gloss_python_function_arbitrary_keyword_arguments.asp
# Ex 1.6.7a
def welcome(**new_person):
print("Welcome " + new_person["first_name"]+" "+new_person["middle_name"]+" "+new_person["last_name"])
welcome(first_name = "Timothy", middle_name="Francis", last_name = "Jones")
welcome(first_name = "Mary", middle_name="", last_name = "Brown")
Welcome Timothy Francis Jones Welcome Mary Brown
In the second call, do we really have to write middle_name="" ? Can't we just leave it out. If you omit defining the middle_name as "" in this script, you will get an error missing middle_name. We know that many predefined Python functions have optional key word arguments. For example, in our recent sort function we called .sort(reverse=True). The default is reverse=False, but we did not have to specify that. How does this work?
**kwargs set up a dictionary of pairs. In the first call the arguments are represented by:
{first_name:"Timothy,middle_name:"Francis",last_name:"Jones"}
Dictionaries are iterable objects. That is we can iterate across items in the dictionary. Each item is a pair (key,value).The key first_name has a value="Timothy". The for loop (lines 3-5) repeats for each items in the dictionary. In the call on line 7 there are three items in the dictionary, Each pass thru the loop greeting is concatenated by adding the value associated with that item pair. Hence, we get the first greeting to Timothy Francis Jones.
In the second call, however, only two key words are specified--the first name and the last name. When the function welcome is called, the loop iterates over these two keys. The result is a greeting to Mary Brown; no middle name is included.
# Ex 1.6.7b
def welcome(**kwargs):
greeting="Welcome "
for (key,value) in kwargs.items():
greeting=greeting+" "+ value
print(greeting)
welcome(first_name = "Timothy", middle_name="Francis", last_name = "Jones")
welcome(first_name = "Mary", last_name = "Brown")
Welcome Timothy Francis Jones Welcome Mary Brown
Objects in Python have names. For example when we write y=1, we create an integer object whose name is y. As we have seen, other objects also have names. For example, animals=[“dog”, “cat”, “aardvark”] is a list object with the name animals. Scope enters the picture when we have modules and submodules in our program. For example, we might have a function to add an animal to our list of animals.
# Ex 1.6.8a
animals=['dog', 'cat', 'aardvark']
print("print line 2 ", animals)
def add_animal():
animals=animals.append('skunk')
print("print inside function ",animals)
add_animal()
print("print after function call ",animals)
print line 2 ['dog', 'cat', 'aardvark']
--------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) Cell In[1], line 8 5 animals=animals.append('skunk') 6 print("print inside function ",animals) ----> 8 add_animal() 9 print("print after function call ",animals) Cell In[1], line 5, in add_animal() 4 def add_animal(): ----> 5 animals=animals.append('skunk') 6 print("print inside function ",animals) UnboundLocalError: cannot access local variable 'animals' where it is not associated with a value
The error that we get refers to a local variable animals. In the function add_animal(), the name animals is treated as a local variable (defined within the scope of the function). It does not matter if the calling program had defined animals (as in line 3). This Python program has two namespaces: one for the main code (lines 3,4,10,& 11) and another for the function (lines 6-8). In the first case animals will be defined as a list object, while in the second, it has no value, therefore, we cannot append anything to it.
We could tell the function add_animal() to look in the namespace of the calling program to find animals. This is done by putting a statement in the function indicating that animals is a global variable. While we can do this, it can be dangerous. More on that later.
# Ex 1.6.8b
animals=['dog', 'cat', 'aardvark']
print("print line 2 ", animals)
def add_animal():
global animals
animals.append('skunk')
print("print inside function ",animals)
add_animal()
print("print after function call ",animals)
print line 2 ['dog', 'cat', 'aardvark'] print inside function ['dog', 'cat', 'aardvark', 'skunk'] print after function call ['dog', 'cat', 'aardvark', 'skunk']
Now the use of the statement global animals tells the Python interpreter to look to the module calling the function to find the name animals.
This solution, however, is not considered good practice. The problem is that when you have large complex programs, you may find that some function embedded in the code is changing a variable that is unexpected and unwanted. Such problems are difficult to debug and the code becomes difficult to maintain. It is a much better idea NOT TO USE GLOBAL, but to pass any name to the function in the argument list.
# Ex 1.6.8c
animals=['dog', 'cat', 'aardvark']
print("print line 2 ", animals)
def add_animal(animals):
animals.append('skunk')
print("print inside function ",animals)
add_animal(animals)
print("print after function call ",animals)
print line 2 ['dog', 'cat', 'aardvark'] print inside function ['dog', 'cat', 'aardvark', 'skunk'] print after function call ['dog', 'cat', 'aardvark', 'skunk']
This is the preferred method for programming in Python. Instead of using animals in the function, you can use anything for example you could replace animals by X. In the call statement (line 9) we pass the list animals to the function. X in the function code refers to the name animals in the calling program.
One final statement. Of course we don't have to ever write a function to add an element to a list. We have done this as an example to discuss the concept of scope and namespaces. Remember that line 6 could have been put in the main program directly. It uses the defined method .append() that applies to lists as wells as other objects.
# Ex 1.6.8d
animals=['dog', 'cat', 'aardvark']
print("print line 2 ", animals)
def add_animal(X):
X.append('skunk')
print("print inside function ",X)
add_animal(animals)
print("print after function call ",animals)
print line 2 ['dog', 'cat', 'aardvark'] print inside function ['dog', 'cat', 'aardvark', 'skunk'] print after function call ['dog', 'cat', 'aardvark', 'skunk']