Unit 4
Unit 4
Functions
In Python, a function is a group of related statements that performs a specific task.
Functions help break our program into smaller and modular chunks. As our program grows larger and
larger, functions make it more organized and manageable.
Furthermore, it avoids repetition and makes the code reusable.
A function is a block of organized, reusable code that is used to perform a single, related action.
Functions provide better modularity for your application and a high degree of code reusing.
Syntax of Function
def function_name(parameters):
"""docstring"""
statement(s)
Example of a function
def greet(name):
"""
This function greets to
the person passed in as
a parameter
"""
print("Hello, " + name + ". Good morning!")
>>> greet('Students')
Hello, Students. Good morning!
Note: Try running the above code in the Python program with the function definition to see the output.
def greet(name):
"""
This function greets to
the person passed in as
a parameter
"""
print("Hello, " + name + ". Good morning!")
greet('Students')
Calling a Function
To call a function, use the function name followed by parenthesis:
Example
def my_function():
print("Pragati Engineering College")
my_function()
Arguments
Information can be passed into functions as arguments.
Arguments are specified after the function name, inside the parentheses. You can add as many
arguments as you want, just separate them with a comma.
The following example has a function with one argument (fname). When the function is called,
we pass along a first name, which is used inside the function to print the full name:
def my_function(fname):
print(fname+ "Students")
my_function("Pragati")
my_function("CSE")
my_function("Section B")
Output:
Pragati Students
CSE Students
Section B Students
Function Arguments
You can call a function by using the following types of formal arguments −
Required arguments
Keyword arguments
Default arguments
Variable-length arguments
Required arguments
Required arguments are the arguments passed to a function in correct positional order. Here, the number
of arguments in the function call should match exactly with the function definition.
To call the function printme(), you definitely need to pass one argument, otherwise it gives a syntax
error
# Function definition is here
def printme( str ):
"This prints a passed string into this function"
print str
return;
Keyword arguments
Keyword arguments are related to the function calls. When you use keyword arguments in a function
call, the caller identifies the arguments by the parameter name.
This allows you to skip arguments or place them out of order because the Python interpreter is able to
use the keywords provided to match the values with parameters. You can also make keyword calls to
the printme() function in the following ways −
#!/usr/bin/python
When the above code is executed, it produces the following result − My string
The following example gives more clear picture. Note that the order of parameters does not matter.
#!/usr/bin/python
Default arguments
A default argument is an argument that assumes a default value if a value is not provided in the function
call for that argument. The following example gives an idea on default arguments, it prints default age if
it is not passed −
#!/usr/bin/python
Variable-length arguments
You may need to process a function for more arguments than you specified while defining the function.
These arguments are called variable-length arguments and are not named in the function definition,
unlike required and default arguments.
Syntax for a function with non-keyword variable arguments is this −
def functionname([formal_args,] *var_args_tuple ):
"function_docstring"
function_suite
return [expression]
An asterisk (*) is placed before the variable name that holds the values of all nonkeyword variable
arguments. This tuple remains empty if no additional arguments are specified during the function call.
Following is a simple example −
#!/usr/bin/python
Syntax
The syntax of lambda functions contains only a single statement, which is as follows −
lambda [arg1 [,arg2,.....argn]]:expression
Following is the example to show how lambda form of function works −
#!/usr/bin/python
Syntax of return
return [expression_list]
This statement can contain an expression that gets evaluated and the value is returned. If there is
no expression in the statement or the return statement itself is not present inside a function, then the
function will return the None object.
All the above examples are not returning any value. You can return a value from a function as follows −
#!/usr/bin/python
if num >= 0:
return num
else:
return -num
print(absolute_value(2))
print(absolute_value(-4))
Output: 2
4
Scope of Variables
All variables in a program may not be accessible at all locations in that program. This depends on where
you have declared a variable.
The scope of a variable determines the portion of the program where you can access a particular
identifier. There are two basic scopes of variables in Python −
Global variables
Local variables
#!/usr/bin/python
Types of Functions
Basically, we can divide functions into the following two types:
1. Built-in functions - Functions that are built into Python.
2. User-defined functions - Functions defined by the users themselves.
Python Recursion
What is recursion?
Recursion is the process of defining something in terms of itself.
A physical world example would be to place two parallel mirrors facing each other. Any object in
between them would be reflected recursively.
def factorial(x):
"""This is a recursive function
to find the factorial of an integer"""
if x == 1:
return 1
else:
return (x * factorial(x-1))
num = 3
print("The factorial of", num, "is", factorial(num))
Output
The factorial of 3 is 6
Advantages of Recursion
1. Recursive functions make the code look clean and elegant.
2. A complex task can be broken down into simpler sub-problems using recursion.
3. Sequence generation is easier with recursion than using some nested iteration.
Disadvantages of Recursion
1. Sometimes the logic behind recursion is hard to follow through.
2. Recursive calls are expensive (inefficient) as they take up a lot of memory and time.
3. Recursive functions are hard to debug.
Object Oriented Programming
Python has been an object-oriented language since it existed. Because of this, creating and using classes
and objects are downright easy. This chapter helps you become an expert in using Python's object-
oriented programming support.
If you do not have any previous experience with object-oriented (OO) programming, you may want to
consult an introductory course on it or at least a tutorial of some sort so that you have a grasp of the basic
concepts.
However, here is small introduction of Object-Oriented Programming (OOP) to bring you at speed −
Creating Classes
The class statement creates a new class definition. The name of the class immediately follows the
keyword class followed by a colon as follows −
class ClassName:
'Optional class documentation string'
class_suite
The class has a documentation string, which can be accessed via ClassName.__doc__.
The class_suite consists of all the component statements defining class members, data attributes
and functions.
Example
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
The variable empCount is a class variable whose value is shared among all instances of a this
class. This can be accessed as Employee.empCount from inside the class or outside the class.
The first method __init__() is a special method, which is called class constructor or initialization
method that Python calls when you create a new instance of this class.
You declare other class methods like normal functions with the exception that the first argument
to each method is self. Python adds the self argument to the list for you; you do not need to
include it when you call the methods.
Accessing Attributes
You access the object's attributes using the dot operator with object. Class variable would be accessed
using class name as follows −
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount
Now, putting all the concepts together −
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
class Employee:
'Common base class for all employees'
empCount = 0
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
Example
This __del__() destructor prints the class name of an instance that is about to be destroyed −
#!/usr/bin/python
class Point:
def __init__( self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print class_name, "destroyed"
pt1 = Point()
pt2 = pt1
pt3 = pt1
print id(pt1), id(pt2), id(pt3) # prints the ids of the obejcts
del pt1
del pt2
del pt3
When the above code is executed, it produces following result −
3083401324 3083401324 3083401324
Point destroyed
Note − Ideally, you should define your classes in separate file, then you should import them in your
main program file using import statement.
Class Inheritance
Instead of starting from scratch, you can create a class by deriving it from a preexisting class by listing
the parent class in parentheses after the new class name.
The child class inherits the attributes of its parent class, and you can use those attributes as if they were
defined in the child class. A child class can also override data members and methods from the parent.
Syntax
Derived classes are declared much like their parent class; however, a list of base classes to inherit from
is given after the class name −
class SubClassName (ParentClass1[, ParentClass2, ...]):
'Optional class documentation string'
class_suite
Example
#!/usr/bin/python
def parentMethod(self):
print 'Calling parent method'
def getAttr(self):
print "Parent attribute :", Parent.parentAttr
Overriding Methods
You can always override your parent class methods. One reason for overriding parent's methods is
because you may want special or different functionality in your subclass.
Example
#!/usr/bin/python
1
__init__ ( self [,args...] )
Constructor (with any optional arguments)
Sample Call : obj = className(args)
2
__del__( self )
Destructor, deletes an object
Sample Call : del obj
3
__repr__( self )
Evaluable string representation
Sample Call : repr(obj)
4
__str__( self )
Printable string representation
Sample Call : str(obj)
5
__cmp__ ( self, x )
Object comparison
Sample Call : cmp(obj, x)
Overloading Operators
Suppose you have created a Vector class to represent two-dimensional vectors, what happens when you
use the plus operator to add them? Most likely Python will yell at you.
You could, however, define the __add__ method in your class to perform vector addition and then the
plus operator would behave as per expectation −
Example
#!/usr/bin/python
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
def __add__(self,other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2,10)
v2 = Vector(5,-2)
print v1 + v2
When the above code is executed, it produces the following result −
Vector(7,8)
Data Hiding
An object's attributes may or may not be visible outside the class definition. You need to name attributes
with a double underscore prefix, and those attributes then are not be directly visible to outsiders.
Example
#!/usr/bin/python
class JustCounter:
__secretCount = 0
def count(self):
self.__secretCount += 1
print self.__secretCount
counter = JustCounter()
counter.count()
counter.count()
print counter.__secretCount
When the above code is executed, it produces the following result −
1
2
Traceback (most recent call last):
File "test.py", line 12, in <module>
print counter.__secretCount
AttributeError: JustCounter instance has no attribute '__secretCount'
Python protects those members by internally changing the name to include the class name. You can
access such attributes as object._className__attrName. If you would replace your last line as following,
then it works for you −
.........................
print counter._JustCounter__secretCount
When the above code is executed, it produces the following result −
1
2
2
Advantages and Disadvantages of Object-Oriented Programming (OOP)
This reading discusses advantages and disadvantages of object-oriented programming, which is a
well-adopted programming style that uses interacting objects to model and solve complex programming
tasks. Two examples of popular object-oriented programming languages are Java and C++. Some other
well-known object-oriented programming languages include Objective C, Perl, Python, Javascript,
Simula, Modula, Ada, Smalltalk, and the Common Lisp Object Standard.
import configparser
config = configparser.ConfigParser()
config.read("config.ini")
login = config['LOGIN']
server = config['SERVER']
You can assign each of the sections into a separate dictionary for easier accessing the values. The
output should be same as below:
Note that the line starting with # symbol (or ; ) will be taken as comment line and omitted when
parsing the keys and values.
Also all the values are taken as string, so you will need to do your own data type conversion after
you read it.
Write to .ini file
Now let’s see how we can write to an ini file.
You will still need this configparser library, and the idea is that you need to set the keys and
values into the configparser object and then save it into a file.
config = configparser.ConfigParser()
if not config.has_section("INFO"):
config.add_section("INFO")
config.set("INFO", "link", "www.codeforests.com")
config.set("INFO", "name", "ken") with open("example.ini", 'w') as configfile:
config.write(configfile)
And this would create the example.ini file with below content:
[INFO]
link = www.codeforests.com
name = ken
Opening a File
It is done using the open() function. No module is required to be imported for this function.
File_object = open(r"File_Name","Access_Mode")
The file should exist in the same directory as the python program file else, full address of the file
should be written on place of filename.
Note: The r is placed before filename to prevent the characters in filename string to be treated as
special character. For example, if there is \temp in the file address, then \t is treated as the tab
character and error is raised of invalid address. The r makes the string raw, that is, it tells that the
string is without any special characters. The r can be ignored if the file is in same directory and
address is not being placed.
file1 = open("MyFile.txt","a")
file2 = open(r"D:\Text\MyFile2.txt","w+")
Here, file1 is created as object for MyFile1 and file2 as object for MyFile2
Closing a file
close() function closes the file and frees the memory space acquired by that file. It is used at the
time when the file is no longer needed or if it is to be opened in a different file mode.
File_object.close()
file1 = open("MyFile.txt","a")
file1.close()
Writing to a file
There are two ways to write in a file.
1. write() : Inserts the string str1 in a single line in the text file.
File_object.write(str1)
2. writelines() : For a list of string elements, each string is inserted in the text file.Used to insert
multiple strings at a single time.
File_object.writelines(L) for L = [str1, str2, str3]
Reading from a file
There are three ways to read data from a text file.
1. read() : Returns the read bytes in form of a string. Reads n bytes, if no n specified, reads the
entire file.
File_object.read([n])
2. readline() : Reads a line of the file and returns in form of a string.For specified n, reads at
most n bytes. However, does not reads more than one line, even if n exceeds the length of the
line.
File_object.readline([n])
3. readlines() : Reads all the lines and return them as each line a string element in a list.
File_object.readlines()
Note: ‘\n’ is treated as a special character of two bytes
Examples:
file1 = open("myfile.txt","w")
L = ["This is Delhi \n","This is Paris \n","This is London \n"]
file1.write("Hello \n")
file1.writelines(L)
file1.close()
file1 = open("myfile.txt","r+")
print("Output of Read function is ")
print(file1.read())
# move to start of file again
file1.seek(0)
print("Output of Readline function is ")
print(file1.readline())
# move to start of file again
file1.seek(0)
print("Output of Read(9) function is ")
print(file1.read(9))
# move to start of file again
file1.seek(0)
print("Output of Readline(9) function is ")
print(file1.readline(9))
# move to start of file again
file1.seek(0)
print("Output of Readlines function is ")
print(file1.readlines())
file1.close()
Output:
Output of Read function is
Hello
This is Delhi
This is Paris
This is London
Appending to a file
file1 = open("myfile.txt","w")
L = ["This is Delhi \n","This is Paris \n","This is London \n"]
file1.close()
# Append-adds at last
file1 = open("myfile.txt","a")#append mode
file1.write("Today \n")
file1.close()
file1 = open("myfile.txt","r")
print("Output of Readlines after appending")
print(file1.readlines())
file1.close()
# Write-Overwrites
file1 = open("myfile.txt","w")#write mode
file1.write("Tomorrow \n")
file1.close()
file1 = open("myfile.txt","r")
print("Output of Readlines after writing")
print(file1.readlines())
file1.close()
Output:
Output of Readlines after appending
['This is Delhi \n', 'This is Paris \n', 'This is London \n', 'Today \n']
The self
Class methods have only one specific difference from ordinary functions - they must have an
extra first name that has to be added to the beginning of the parameter list, but you do not give a
value for this parameter when you call the method, Python will provide it. This particular
variable refers to the object itself, and by convention, it is given the name self.
Although, you can give any name for this parameter, it is strongly recommended that you use the
name self.
The self in Python is equivalent to the this pointer in C++ and the this reference in Java and C#.
If you have a method which takes no arguments, then you still have to have one argument -
the self.
Classes
The simplest class possible is shown in the following example (save as oop_simplestclass.py).
class Person:
pass # An empty block
p = Person()
print(p)
Output:
$ python oop_simplestclass.py
<__main__.Person instance at 0x10171f518>
Notice that the address of the computer memory where your object is stored is also printed. The
address will have a different value on your computer since Python can store the object wherever
it finds space.
Methods
Classes/objects can have methods just like functions except that we have an extra self variable.
We will now see an example (save as oop_method.py).
class Person:
def say_hi(self):
print('Hello, how are you?')
p = Person()
p.say_hi()
Output:
$ python oop_method.py
Hello, how are you?
def say_hi(self):
print('Hello, my name is', self.name)
p = Person('Swaroop')
p.say_hi()
Output:
$ python oop_init.py
Hello, my name is Swaroop
def die(self):
print("{} is being destroyed!".format(self.name))
Robot.population -= 1
if Robot.population == 0:
print("{} was the last one.".format(self.name))
else:
print("There are still {:d} robots working.".format(Robot.population))
def say_hi(self):
print("Greetings, my masters call me {}.".format(self.name))
@classmethod
def how_many(cls):
"""Prints the current population."""
print("We have {:d} robots.".format(cls.population))
droid1 = Robot("R2-D2")
droid1.say_hi()
Robot.how_many()
droid2 = Robot("C-3PO")
droid2.say_hi()
Robot.how_many()
Robot.how_many()
Output:
$ python oop_objvar.py
Greetings, my masters call me R2-D2.
We have 1 robots.
Greetings, my masters call me C-3PO.
We have 2 robots.
Inheritance
One of the major benefits of object oriented programming is reuse of code and one of the ways
this is achieved is through the inheritance mechanism. Inheritance can be best imagined as
implementing a type and subtype relationship between classes.
To make a class inherit from another, we apply the name of the base class in parentheses to the
derived class’ definition.
class Person:
pass
class Student(Person):
pass
here, student is a subclass of person.
class Teacher(SchoolMember):
def __init__(self, name, age, salary):
SchoolMember.__init__(self, name, age)
self.salary = salary
def tell(self):
SchoolMember.tell(self)
print('Salary: "{:d}"'.format(self.salary))
class Student(SchoolMember):
'''Represents a student.'''
def __init__(self, name, age, marks):
SchoolMember.__init__(self, name, age)
self.marks = marks
def tell(self):
SchoolMember.tell(self)
print('Marks: "{:d}"'.format(self.marks))
members = [t, s]
for member in members:
# Works for both Teachers and Students
member.tell()
Output:
$ python oop_subclass.py
Name:"Mrs. Shrividya" Age:"40" Salary: "30000"
Name:"Swaroop" Age:"25" Marks: "75"
lime=citrus()
I’m a fruit
I’m citrus
b. Multiple Inheritance
Multiple inheritance are when a class inherits from multiple base classes.
class Color:
pass
class Fruit:
pass
class Orange(Color,Fruit):
pass
c. Multilevel Inheritance
When one class inherits from another, which in turn inherits from another, it is multilevel python
inheritance.
class A:
x=1
class B(A):
pass
class C(B):
pass
cobj=C()
cobj.x
1
d. Hierarchical Inheritance
When more than one class inherits from a class, it is hierarchical Python inheritance.
class A:
pass
class B(A):
pass
class C(A):
pass
e. Hybrid Inheritance
Hybrid Python inheritance is a combination of any two or more kinds of inheritance.
class A:
x=1
class B(A):
pass
class C(A):
pass
class D(B,C):
pass
dobj=D()
dobj.x
1
Method Overriding
A subclass may change the functionality of a Python method in the superclass. It does so by
redefining it. This is termed python method overriding. Lets see this Python Method Overriding
Example.
class A:
def sayhi(self):
print("I'm in A")
class B(A):
def sayhi(self):
print("I'm in B")
bobj=B()
bobj.sayhi()
output:
I’m in B
Data Hiding
Data hiding is one of the important features of Object Oriented Programming which allows
preventing the functions of a program to access directly the internal representation of a class
type. By default all members of a class can be accessed outside of class.
You can prevent this by making class members private or protected.
In Python, we use double underscore (__) before the attributes name to make those
attributes private.
We can use single underscore (_) before the attributes name to make those attributes protected.
class MyClass:
__hiddenVariable = 0 # Private member of MyClass
_protectedVar = 0 # Protected member of MyClass
myObject = MyClass()
myObject.add(2)
myObject.add(5)
# A Python program to demonstrate that hidden members can be accessed outside a class
class MyClass:
__hiddenVariable = 10 # Hidden member of MyClass
myObject = MyClass()
print(myObject._MyClass__hiddenVariable)
Private methods are accessible outside their class, just not easily accessible. Nothing in Python is
truly private.
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1+p2)
Output
Here, we can see that a TypeError was raised, since Python didn't know how to add
two Point objects together.
class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y
def __str__(self):
return "({0},{1})".format(self.x,self.y)
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "({0}, {1})".format(self.x, self.y)
p1 = Point(2, 3)
print(p1)
Output
(2, 3)
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "({0},{1})".format(self.x, self.y)
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "({0},{1})".format(self.x, self.y)
p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1+p2)
Output
(3,5)
What actually happens is that, when you use p1 + p2, Python calls p1.__add__(p2) which in turn
is Point.__add__(p1,p2). After this, the addition operation is carried out the way we specified.
Similarly, we can overload other operators as well. The special function that we need to
implement is tabulated below.
Operator Expression Internally
Addition p1 + p2 p1.__add__(p2)
Subtraction p1 - p2 p1.__sub__(p2)
Multiplication p1 * p2 p1.__mul__(p2)
Power p1 ** p2 p1.__pow__(p2)
Division p1 / p2 p1.__truediv__(p2)
Bitwise OR p1 | p2 p1.__or__(p2)
def __str__(self):
return "({0},{1})".format(self.x, self.y)
p1 = Point(1,1)
p2 = Point(-2,-3)
p3 = Point(1,-1)
Output
True
False
False
Similarly, the special functions that we need to implement, to overload other comparison
operators are tabulated below.
Operator Expression Internally
Equal to p1 == p2 p1.__eq__(p2)