...
...
Object Oriented Programming OOP in Python: Classes, Self Variable, Methods, Constructor
Method, Inheritance , Overriding Methods, Data hiding.
Error and Exceptions: Difference between an error and Exception, Handling except block,
Raising Exceptions, User Defined Exceptions.
class ClassName(BaseClasses):
'Optional class documentation string'
Statements inside the class
The variable student_count is a class variable whose value is shared among all instances
of this class. This can be accessed as Student.student_count 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 we create a new instance of this class.
We can declare other class methods like normal functions with the exception that the first
argument to each method is self.
Example:
stud1 = Student(‘Dwij’, 'sai')
This would create a first object of Student class.
stud2 = Student('Nikhil', 'sai')
This would create a second object of Student class.
Accessing Attributes
We can access the object's attributes using the dot operator with object. Class variable
would be accessed using class name as follows:
Print(stud1.email)
Print(stud2.fullname())
print("Total Students:" Student.student_count)
Instead of using the normal statements to access attributes, you can use the following functions:
1. The getattr(obj,name) : to access the attribute of object.
2. The hasattr(obj,name) : to check if an attribute exists or not.
3. The setattr(obj,name,value) : to set an attribute. If attribute does not exist, then it would be
created.
4. The delattr(obj,name) : to delete an attribute.
Example(s):
hasattr(stud1, 'age')
# Returns true if 'age' attribute exists
getattr(stud1, 'age')
# Returns value of 'age' attribute
setattr(stud1, 'age', 8)
# Set attribute 'age' at 8
delattr(studl, 'age')
# Delete attribute 'age
Example:
class Student:
student_count = 0
def __init__(self, first, last):
self.fname = first
self.lname = last
self.email = self.fname + "." + self.lname + "@company.com"
Student.student_count += 1
def fullname(self):
return "{} {}".format(self.fname, self.lname)
def display_count(self):
print("Total Number of Students:",Student.student_count)
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:
Example:
class Vehicle:
def general_usage(self):
print("General use:Transportation")
class Car(Vehicle):
def __init__(self):
print("I am Car")
self.wheels = 4
self.has_roof = True
def specific_usage(self):
print("Specific use: Commute to work, vacation with family")
class MotorCycle(Vehicle):
def __init__(self):
print("I am MotorCycle")
self.wheels = 2
self.has_roof = False
def specific_usage(self):
print("Specific usage: road trip, racing")
c = Car()
c.general_usage()
c.specific_usage()
m = MotorCycle()
m.general_usage()
m.specific_usage()
When the above code is executed, it produces the following Output:
I am Car
General use:Transportation
Specific use: Commute to work, vacation with family
I am MotorCycle
General use:Transportation
Specific usage: road trip, racing
Multiple Inheritance:
Multiple inheritance is a feature in which a class can inherit attributes and methods from
more than one parent class. Java doesn't even support multiple inheritance, while C++ supports
it. Python has a sophisticated and well-designed approach to multiple inheritance.
Example:
class Father:
def gardening(self):
print("I enjoy gardening")
class Mother:
def cooking(self):
print("I love cooking")
class Child(Father, Mother):
def sports(self):
print("I enjoy sports")
c = Child()
c.gardening()
c.cooking()
c.sports()
When the above code is executed, it produces the following Output:
I enjoy gardening
I love cooking
I enjoy sports
Multilevel Inheritance:
We can also inherit form a derived class. This is called multilevel inheritance. It can be of
any depth in Python.
In multilevel inheritance, features of the base class and the derived class are inherited into
the new derived class.
Example:
Class Base:
Pass
class Derived1(Base):
pass
class Derived2(Derived1):
pass
issubclass(sub, sup) is a boolean function returns True if the given subclass sub is indeed a
subclass of the superclass sup.
isinstance(obj, Class) is a boolean function returns True if obj is an instance of class Class or
is an instance of a subclass of Class.
Example:
class Father:
def hobby(self):
print("I enjoy gardening")
class Mother:
def hobby(self):
print("I love cooking")
class Child(Father, Mother):
def hobby(self):
print("I enjoy sports")
c = Child()
c.hobby()
When the above code is executed, it produces the following Output:
I enjoy sports
Example:
class A:
__count = 0
def count(self):
self.__count += 1
print(self.__count)
a = A()
a.count()
a.count()
print(a.__count)
When the above code is executed, it produces the following result:
1
2
Traceback (most recent call last):
File "C:\Users\Pavani\AppData\Local\Programs\Python\Python36-32\sample13.py", line 9, in
<module>
print(a.__count)
AttributeError: 'A' object has no attribute '__count'
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(a._A__count)
When the above code is executed, it produces the following Output:
1
2
2
Example:
a = 40 #Creates object <40>
b=a #Increases ref. count of <40>
c = [b,80] #Increases ref. count of <40>
del a #Decreases ref. count of <40>
b = 100 #Decreases ref. count of <40>
c[0] = 20 #Decreases ref. count of <40>
We normally will not notice when the garbage collector destroys an orphaned instance
and reclaims its space. However, a class can implement the special method__del__(), called a
destructor, that is invoked when the instance is about to be destroyed.
Example:
class A:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
print("Destructor Called")
a = A()
a1 = a
a2 = a1
print(id(a),id(a1),id(a2))
del a,a1,a2
When the above code is executed, it produces the following output:
23862320 23862320 23862320
Destructor Called
Q) Write the difference between an error and Exception.
Until now error messages haven‘t been more than mentioned, but if you have tried out the
examples you have probably seen some. There are (at least) two distinguishable kinds of errors:
syntax errors and exceptions.
1. Syntax Errors:
Syntax errors, also known as parsing errors, are perhaps the most common kind of
complaint you get while you are still learning Python:
The parser repeats the offending line and displays a little arrow pointing at the earliest
point in the line where the error was detected. The error is caused by (or at least detected at) the
token preceding the arrow: in the example, the error is detected at the function print(), since a
colon (':') is missing before it. File name and line number are printed so you know where to look
in case the input came from a script.
2. Exceptions:
Even if a statement or expression is syntactically correct, it may cause an error when an
attempt is made to execute it. Errors detected during execution are called Exceptions and are not
unconditionally fatal. Most exceptions are not handled by programs, however, and result in error
messages as shown here:
>>>10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>>4 + a*3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>>'2' + 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't convert 'int' object to str implicitly
The last line of the error message indicates what happened. Exceptions come in different
types and the type is printed as part of the message.
The types in the example are ZeroDivisionError, NameError and TypeError. The string
printed as the exception type is the name of the built-in exception that occurred. This is true for
all built-in exceptions, but need not be true for user-defined exceptions. Standard exception
names are built-in identifiers (not reserved keywords). The rest of the line provides detail based
on the type of exception and what caused it. The preceding part of the error message shows the
context where the exception happened, in the form of a stack traceback. In general it contains a
stack traceback listing source lines. However, it will not display lines read from standard input.
Error handling:
Python raises exceptions whenever it detects errors in programs at runtime. We can catch
and respond to the errors in our code, or ignore the exceptions that are raised. If an error is
ignored, Python‘s default exception-handling behavior kicks in. It stops the program and prints
an error message. If we don‘t want this default behavior, code a try statement to catch and
recover from the exception. Python will jump to our try handler when the error is detected, and
our program will resume execution after the try.
Event notification:
Exceptions can also be used to signal valid conditions without you having to pass result
flags around a program or test them explicitly. For instance, a search routine might raise an
exception on failure, rather than returning an integer result code and hoping that the code will
never be a valid result.
Special-case handling:
Sometimes a condition may occur so rarely that it‘s hard to justify convoluting your code
to handle it in multiple places. You can often eliminate special-case code by handling unusual
cases in exception handlers in higher levels of your program.
Termination actions:
The try/finally statement allows you to guarantee that required closing-time operations
will be performed, regardless of the presence or absence of exceptions in your programs. The
newer with statement offers an alternative in this department for objects that support it.
>>>10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>>4 + a*3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>>'2' + 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't convert 'int' object to str implicitly
The last line of the error message indicates what happened. Exceptions come in different
types and the type is printed as part of the message.
The types in the example are ZeroDivisionError, NameError and TypeError. The string
printed as the exception type is the name of the built-in exception that occurred. This is true for
all built-in exceptions, but need not be true for user-defined exceptions.
Handling Exceptions:
If we have some suspicious code that may raise an exception, we can handle our program
by placing the suspicious code in a try: block.
After the try: block, include an except: statement, followed by a block of code which
handles the problem as elegantly as possible.
Syntax:
Here is simple syntax of try....except...else blocks:
try:
Statements which may cause an exception
except ExceptionI:
If there is ExceptionI, then execute this block.
except ExceptionII:
If there is ExceptionII, then execute this block.
......................
else:
If there is no exception then execute this block.
Here are few key points about the above mentioned syntax:
1. A single try statement can have multiple except statements. This is useful when the try
block contains statements that may throw different types of exceptions.
2. We can also provide a generic except clause, which handles any exception.
3. After the except clause(s), we can include an else-clause. The code in the else-block
executes if the code in the try: block does not raise an exception.
4. The else-block is a good place for code that does not need the try: block's protection.
Example.1:
This example opens a file, writes content in the, file and comes out gracefully because
there is no problem at all:
try:
fh = open("testfile", "w")
fh.write("This is my test file for exception handling!!")
except IOError:
print("Error: can\'t find file or read data")
else:
print("Written content in the file successfully")
fh.close()
This kind of a try-except statement catches all the exceptions that occur. Using this kind
of try except statement is not considered a good programming practice though, because it catches
all exceptions but does not make the programmer identify the root cause of the problem that may
occur.
Example:
import sys
try:
a = int(input("Enter the value of a:"))
b = 1/a
except:
print("OOPS!",sys.exc_info()[0],"occured")
else:
print(b)
Example:
try:
a = int(input("Enter the value of a:"))
b = 1/a
except(ValueError,ZeroDivisionError):
print("There may be ValueError or ZeroDivisionError occured")
else:
print(b)
Note that we can provide except clause(s), or a finally clause, but not both. We cannot
use else clause as well along with a finally clause.
Example:
try:
fh = open("testfile", "w")
try:
fh.write("This is my test file for exception handling!!")
finally:
print("Going to close the file")
fh.close()
except IOError:
print("Error: can\'t find file or read data")
When an exception is thrown in the try block, the execution immediately passes to the
finally block. After all the statements in the finally block are executed, the exception is raised
again and is handled in the except statements if present in the next higher layer of the try-except
statement.
If we write the code to handle a single exception, we can have a variable follow the name of the
exception in the except statement. This variable receives the value of the exception mostly
containing the cause of the exception. The variable can receive a single value or multiple values
in the form of a tuple. This tuple usually contains the error string, the error number, and an error
location.
Example:
def temp_convert(var):
try:
return int(var)
except ValueError as Argument:
print("The argument does not contain numbers\n", Argument)
#call the above function here
print(temp_convert("xyz"));
Output:
The argument does not contain numbers
invalid literal for int() with base 10: 'xyz'
Example:
An exception can be a string, a class or an object. Most of the exceptions that the Python
core raises are classes, with an argument that is an instance of the class. Defining new exceptions
is quite easy and can be done as follows:
def functionName(level):
if level <1:
raise Exception(level)
# The code below to this would not be executed
# if we raise the exception
return level
try:
l=functionName(-10)
print("level=",l)
except Exception as e:
print ("error in level argument",e.args[0])
Output:
error in level argument -10
class Networkerror(RuntimeError):
def __init__(self, arg):
self.args = arg