Functions, Modules, Classes, and Files

This section provides a quick overview of the following topics:

  • Functions
  • Modules
  • Classes

Functions

Functions are reusable code blocks. Functions have specific names and can be called any number of times during a program. Functions make the code more modular. Functions may or may not return objects. The following is an example of a function which adds two numbers.

In [1]:
def add2(x,y):
    a = x + y
    return a

b = add2(5,10)
print(b)
print(add2(20,10))
15
30

Functions are defined using the def keyword followed by the function name and paranthesis. In the above example, add2 is the function name. The paranthesis usually contains the arguments or parameters which are inputs for the code blocks inside the function. For the above example, there are two parameters x and y. The function also contains a return statement which passes an object back to the main program. In the above example, the function returns a which contains the sum of x and y.

Functions need to be defined and called. When you define a function, you are writing down all the codes inside the function. When you call a function, you are executing the function.

Functions need not always return values. If there is no return values, python returns an object called "None". For example, the function Hello_World() returns an object "None".

In [2]:
def Hello_World():
    print("Hello World")
    return

Hello_World()
Hello World

The function listadd returns a list.

In [3]:
def listadd(x):
    x.append(5)
    return x

a = [10, 15]
b = listadd(a)
print(b) 
[10, 15, 5]

Functions can return more than one object to the main program. In this case functions return two values a and b. The value of a is stored in d and the value of b is stored in e.

In [4]:
def add2mult2(x,y):
    a = x + y
    b = x*y
    return a, b

d, e = add2mult2(10,20)
print("d =", d)
print("e =", e)
d = 30
e = 200

The text between triple quotes is called docstrings. It is not needed but it is a good habit to write the docstrings when you create a function. Often contains details on what calculation is performed, what are the input parameters, what is returned. You can read the docstrings using the help command.

In [5]:
def add2mult2(x,y):
    """
    This functions adds and multiplies two numbers x and y.
    The function returns a which is the sum of x and y.
    The function returns b which is the product of x and y.
    """
    a = x + y
    b = x*y
    return a, b

help(add2mult2)
Help on function add2mult2 in module __main__:

add2mult2(x, y)
    This functions adds and multiplies two numbers x and y.
    The function returns a which is the sum of x and y.
    The function returns b which is the product of x and y.

Functions can be nested.

In [6]:
def firstline():
    print("Twinkle Twinkle Little Star")
    return

def secondline():
    print("How I wonder what you are")
    return

def first2lines():
    firstline()
    secondline()
    return

first2lines()  
Twinkle Twinkle Little Star
How I wonder what you are

Functions can be called within the same function. The fact function is an example of recursive function where the same function is called within the function.

In [7]:
def fact(x):
    if x == 0:
        return 1
    else:
        return x*fact(x-1)

b = fact(5)
print(b)
120

For a variable, changes made within the function will not affect its value outside of the function. In the example given below, even though we are doubling the value of x inside the function, the value of x outside the function remains the same. Variables defined in the function have local scope.

In [8]:
x = 10
def doublex(x):
    x = x*2
    print("x inside  ", x)
    return x

print("x function return ", doublex(x))
print("x outside  ", x)
x inside   20
x function return  20
x outside   10

Functions can modify mutable objects like lists.

In [9]:
a = [10, 15]
def listadd(x):
    x.append(5)
    return 

listadd(a)
print(a) 
[10, 15, 5]

If you dont want the list to be modified outside the function, pass a copy using slicing.

In [10]:
a = [10, 15]
b = [10, 15]

def listadd(x):
    x.append(5)
    return 

listadd(a)
listadd(b[:])
print("printing list a ", a) 
print("printing list b ", b) 
printing list a  [10, 15, 5]
printing list b  [10, 15]

Note the difference when you use a list method and the assignment operator.

In [11]:
a = [10, 15]
b = [10, 15]

def listadd(x):
    x.append(5)
    print("Inside list add", x)
    return 

def listadd1(x):
    x = x*2
    print("Inside list add1", x)
    return 

listadd(a)
listadd1(b)
print("Outside function list a ", a) 
print("Outside function list b ", b) 
Inside list add [10, 15, 5]
Inside list add1 [10, 15, 10, 15]
Outside function list a  [10, 15, 5]
Outside function list b  [10, 15]

Functions can also be used in the case where the number of arguments are not fixed.

In [12]:
def mult(*x, **y):
    product = 1
    for i in x:
        product = product*i
    for i,j in y.items():
        product = product*j
    return product

print(mult(1, 2, 3, austin=4, pittsburgh=5))
120

When we have an argument called *arg, then it is collected as a tuple. When we have an argument as **arg, then the corresponding values are colelcted as a dictionary.

Lambda function is a compact representation.

In [13]:
total = lambda x, y: x + y # defining a function called total with two arguments x and y
mult = lambda x, y, z: x*y + z # defining a function called mult with three arguments x,y, and z

print("First Time ", total(5,10)) 
print("Second Time ", total([1,2],[3,4])) 
print("First time mult is called ", mult(5,10,20)) 
First Time  15
Second Time  [1, 2, 3, 4]
First time mult is called  70

Map command applies a function to each element of a sequence and returns a new sequence.

In [14]:
square = lambda x: x*x;
a = [10, 11, 12]
b = list(map(square, a))
print(b)
[100, 121, 144]
In [15]:
c = [20, 10, 50]
d = [ 5, 25, 40]
e = list(map(max, zip(c,d)))
print(e)
[20, 25, 50]

Map can be applied to more than one list.

In [16]:
sum = lambda x, y: x + y;
e = list(map(sum, c, d))
print(e)
[25, 35, 90]

Filter command filters out the elements of the list for which the function returns True.

In [17]:
even = lambda x: x%2 == 0;
a = [10, 11, 12]
b = list(filter(even, a))
print(b)
[10, 12]

Modules

A python module is a file containing python variables, statements, and functions. You can write your own python scrpit, save it as a filename.py, place it in the same directory as your current code and use it using the import function. Python has a Standard Library which is a collection of modules providing it with the various functionality. For example, the math module provides us access to the various math functions.

For example, the code below provides us access to the logarithm function from the math module.

In [18]:
import math
x = math.log(10)
print(x)
2.302585092994046

If we want to avoid using math prefix, we can import everything in the math module as shown below.

In [19]:
from math import *
x = log(10)
print(x)
2.302585092994046

The above approach is not recommended for large programs. Ideally you import only the functions you need.

In [20]:
from math import log
x = log(10)
print(x)
2.302585092994046

Another way to import is shown below. The code below provides access to all functions in math module.

In [21]:
import math as mt
x = mt.log(10)
print(x)
2.302585092994046

Classes

Classes can be thought of as a collection of variables, data structures, and functions. Classes can be used to design new objects. Classes help organize variables, data structures, and functions which operate on these variables and data structure in a compact manner

In [22]:
class Student:
    studentCount = 0
    def __init__(self, name, exam1, exam2):
        self.name = name
        self.exam1 = exam1
        self.exam2 = exam2
        Student.studentCount = Student.studentCount + 1
    def displayStudent(self):
        print("Name : ", self.name, ", Score in Exam 1: ", self.exam1, ", Score in Exam 2: ", self.exam2)
    def displayStudentCount(self):
        print("Total Number of Students: ", Student.studentCount)
    def examAverage(self):
        return 0.5*(self.exam1 + self.exam2)

a = Student("Tom", 80, 90)
print("Student Count is ", a.studentCount)
b = Student("Stacy", 90, 100)
c = Student("Joe", 70, 90)

print(a.name, a.examAverage())
a.displayStudentCount()

a.displayStudent()
b.displayStudent()
c.displayStudent()
Student Count is  1
Tom 85.0
Total Number of Students:  3
Name :  Tom , Score in Exam 1:  80 , Score in Exam 2:  90
Name :  Stacy , Score in Exam 1:  90 , Score in Exam 2:  100
Name :  Joe , Score in Exam 1:  70 , Score in Exam 2:  90

Classes are defined using the class keyword. In the above example Student is the name of the class. Note that Class names start with capital letters by convention. All the elements or members of the class are indented. In the first line we define a variable studentCount which is a member of the class and set it equal to 0.

The next line contains a function called init. This function is called a constructor which is called by default whenever you create an object of the class type. Constructors are used to initialize the variables or data structures defined in the class.

Note that the first argument or parameter in any function defined in a class is self. In the above program, an object of the type Student is created when you run the line a = Student("Tom", 80, 90). When an object is created, the constructur is called which initializes a.name to Tom, a.exam1 to 80, and a.exam2 to 90. The studentCount variable is increased to 1.

In the above code, we initialize three objects a,b, and c. The class members are accessed using the dot notation - ObjectName.VariableName or ObjectName.FunctionName(Parameters).

In the above class, we have three functinos other than the constructor. The second function is to display information about the student. The third function displays the number of students and the fourth function returns the average score in two examinations.