Oops Concepts in Python
Oops Concepts in Python
Oops Concepts in Python
We can think an object as an entity that resides in memory, has a state and it's able to perform
some actions.
More formally objects are entities that represent instances of a general abstract concept called
class. In Python , "attributes" are the variables defining an object state and the possible actions
are called "methods".
Each class represents meaningful view of the objects that are instances of this class, without
going into too much detail or giving others access to the inner workings of the objects i.e. how it
works internally. The class contains instance variables, also known as data members as well as
some methods for some purpose also known as member functions. Object of the class can
access this methods to achieve something meaningful.
2) We need to write in such a way that it handles complex programs and it can handle with
unexpected data.
Every developer develop application in such a way that an application will give correct output in
all the scenarios. Our application will be robust, that is, it should capable enough for handling
unexpected data that is not predefined. For example, if a program is expecting a string and
instead it will receive negative integer, then the program should be able to recover and handle
this type of error gracefully.
Writing software programs is a style of programming that dealing with surprising and
unforeseen activities. It expects code to deal with these types of terminations and expectations
and run smmothly.
Adaptability
1) Software program or application will grow over a life time
2) The application should build such a way that is should run with different versions, different
generations, different hardware as well.
Software program or application should have the option to advance after some time in light of
changing conditions. Along these lines, this is significant objective of value in programming that
flexibility ought to be accomplished. Program should be build in such a way that it will run with
minor change on various hardware and os platforms.
Reusability
Building from reusable applications to avoid making entire new application just like rebuilding
entire wheel.
Software should be reusable, that is, the same code should be reusable as a component for
different applications. Developing quality software application can be an expensive and if the
software is designed in a way that makes it easily reusable in future applications, it will helpful
in productivity and also can reduce cost for new application.
class ClassName(base_classes):
statements
Name
Surname
Age
class Person:
pass
obj1 = Person()
obj1.name = "Ram"
obj1.surname = "Sham"
obj1.year_of_birth = 1958
obj1
print(obj1)
print("%s %s was born in %d." %
(obj1.name, obj1.surname, obj1.year_of_birth))
pass is generally used where code will eventually run without any errors.
class Person:
pass
The following example defines an empty class (i.e. the class doesn't have a state) called Person
then creates a Person instance called john_doe and adds three attributes to john_doe. We see
that we can access objects attributes using the "dot" operator.
This isn't a recommended style because classes should describe homogeneous entities. A way
to do so is the following:
class Person:
def __init__(self, name, surname, year_of_birth):
self.name = name
self.surname = surname
self.year_of_birth = year_of_birth
__init__(self, ...)
Is a special Python method that is automatically called after an object construction. Its purpose
is to initialize every object state. The first argument (by convention) self is automatically passed
either and refers to the object itself.
In the preceding example, __init__ adds three attributes to every object that is instantiated. So
the class is actually describing each object's state.
Attributes created in __init__ method are called instance attributes, so name,surname and
year_of_birth are called instance attributes.
class Person:
# Here we can declare class attributes
company = "xyz"
obj.company
type(obj)
id(obj)
obj.show()
obj.surname
print(obj)
Here comapny is called class variables and we can use class variables if it will not change for
entire class and their methods and functions. It should be same for entire class and every
instance. We can use instance attributes which will vary from one instance to another instances
of the class.
We cannot directly manipulate any class rather we need to create an instance of the class:
We have just created an instance of the Person class, bound to the variable obj .
# Another example
class Student:
def __init__(self,first_name,last_name,age,class_,section):
self.first_name = first_name
last_name = last_name
self.age = age
self.class_ = class_
self.section = section
print(last_name)
def show(self):
print(self.first_name,self.last_name)
student1 = Student("Ram","Kumar",17,10,"A")
student1.show()
student2 = Student("Shyam","Sharma",16,9,"C")
keyboard_arrow_down Methods
class Person:
def __init__(a, name, surname, year_of_birth):
a.name = name
a.surname = surname
a.year_of_birth = year_of_birth
def __str__(a):
return "%s %s was born in %d ." % (a.name, a.surname, a.year_of_birth)
We defined two more methods age and __str__ . The latter is once again a special method that
is called by Python when the object has to be represented as a string (e.g. when has to be
printed). If the __str__ method isn't defined the print command shows the type of object and
its address in memory. We can see that in order to call a method we use the same syntax for
attributes (instance_name.instance _method).
.init() and .str() methods are called dunder methods. We can call as dunder methods beacause
they begin and end with double underscores.
#Let's look at another database example for class methods
# For more detail about database, we will look in future classes.
import sqlite3
class DataBaseOperations:
def __init__(self,databasename):
self.databasename = databasename
def createDatabase(self):
try:
conn = sqlite3.connect(self.databasename)
except ConnectionError:
raise ConnectionError
return conn
def createTable(self,tablename,dictionaryOfcolumnNamesAndcolumnDatatypes):
try:
conn = self.createDatabase()
c = conn.cursor()
for key in dictionaryOfcolumnNamesAndcolumnDatatypes.keys():
datatype = dictionaryOfcolumnNamesAndcolumnDatatypes[key]
try:
conn.execute(
'ALTER TABLE {tableName} ADD COLUMN "{column_name}" {dataType}'.f
except:
conn.execute('CREATE TABLE {tableName} ({column_name} {dataType})'.fo
def selectFromTable(self,tablename):
try:
conn = self.createDatabase()
c = conn.cursor()
c.execute("SELECT * FROM {table}".format(table=tablename))
print("values in table : " ,c.fetchall())
self.closeDbConnection(conn)
print("Connection to database closed!!")
except Exception as e:
self.closeDbConnection(conn)
print("Connection to database closed!!")
print("Error occured: " + str(e))
def closeDbConnection(self,connection):
connection.close()
#creating database
db.createDatabase()
db.createTable("table1",tableDetails)
valuesToisnert= ('1,1,97')
# Inserting values
db.insertIntoTable("table1",valuesToisnert)
db.selectFromTable("table1")
def __str__(self):
return "%s %s was born in %d ." \
% (self.name, self.surname, self.year_of_birth)
In this case, an empty instance of the class Person is created, and no attributes have been
initialized while instantiating:
president = Person()
president.set_name('John')
president.set_surname('Doe')
president.set_year_of_birth(1940)
Moreover prepending two underscores to a variable name makes the interpreter mangle a little
the variable name.
class Person:
def __init__(self, name, surname, year_of_birth):
self._name = name # _ single underscore means protected
self._surname = surname
self._year_of_birth = year_of_birth
def __str__(self):
return "%s %s and was born %d." \
% (self._name, self._surname, self._year_of_birth)
class Person:
def __init__(a, name, surname, year_of_birth): # we can give whichever name we can gi
a.__name = name # __ double underscore means private members of a class
a.__surname = surname
a.__year_of_birth = year_of_birth
def __str__(self):
return "%s %s and was born %d." \
% (self.__name, self.__surname, self.__year_of_birth)
__dict__ is a special attribute is a dictionary containing each attribute of an object. We can see
that prepending two underscores every key has _ClassName__ prepended.
Through inheritance one can take all the methods and attributes from the another class and we
can override or extend the methods from the another class. The class which is overriding is
called child class and the class from methods are taking is called parent class.
class Student(Person):
def __init__(self, student_id, *args):
super(Student, self).__init__(*args)
self._student_id = student_id
Charlie now has the same behavior of a Person, but his state has also a student ID. A Person is
one of the base classes of Student and Student is one of the sub classes of Person. Be aware
that a subclass knows about its superclasses but the converse isn't true.
A sub class doesn't only inherits from its base classes, but from its base classes too, forming an
inheritance tree that starts from a object (every class base class).
super(Class, instance)
is a function that returns a proxy-object that delegates method calls to a parent or sibling class
of type. So we used it to access Person's __init__ .
# Another example
class StudentMarks(DataBaseOperations): # inheriting the DatabaseOperation class
self.id= ID
self.RollNum = RollNumber
self.Marks = Marks
self.databasename = "StudentDetails"
student1 = StudentMarks(23,34,76)
student1.createDatabase()
student1.createTable("studentMarks2",tableDetails)
valuestoInsert= ("{0},{1},{2}".format(student1.id,student1.RollNum,student1.Marks))
student1.insertIntoTable("studentMarks2",valuestoInsert)
student1.selectFromTable("studentMarks2")
class Student(Person):
def __init__(self, student_id, *args, **kwargs):
super(Student, self).__init__(*args, **kwargs)
self._student_id = student_id
def __str__(self):
return super(Student, self).__str__() + " And has ID: %d" % self._student_id
We defined __str__ again overriding the one wrote in Person, but we wanted to extend it, so we
used super to achieve our goal.
#another class inheriting the DataBaseOperation class with overRiding the insert function
class StudentDetails(DataBaseOperations): # inheriting the DatabaseOperation class
self.FirstName= FirstName
self.LastName = LastName
self.RollNumber = RollNumber
self.Class = Class
self.databasename = "StudentDetails"
#overriding the insert method of parent class to insert string values in table
def insertIntoTable(self,tablename):
try:
listOfvaluesToInsert= ("{0},{1},{2},{3}".format(firstName,LastName,self.R
conn = self.createDatabase()
conn.execute('INSERT INTO {tablename} values ({values})'.format(tablenam
conn.commit()
print("Values Inserted Successfully!!!")
self.closeDbConnection(conn)
print("Connection to database closed!!")
except Exception as e:
conn.rollback()
self.closeDbConnection(conn)
print("Connection to database closed!!")
print("Error occured: " + str(e))
student1 = StudentDetails("Raj","Kumar",34,"Ten")
student1.createDatabase()
student1.createTable("studentDetails",tableDetails)
student1.insertIntoTable("studentDetails")
student1.selectFromTable("studentDetails")
Composition
Dynamic Extension
keyboard_arrow_down Composition
The abstraction process relies on creating a simplified model that remove useless details from
a concept. In order to be simplified, a model should be described in terms of other simpler
concepts. For example, we can say that a car is composed by:
Tyres
Engine
Body
And break down each one of these elements in simpler parts until we reach primitive data.
class Tyres:
def __init__(self, branch, belted_bias, opt_pressure):
self.branch = branch
self.belted_bias = belted_bias
self.opt_pressure = opt_pressure
def __str__(self):
return ("Tyres: \n \tBranch: " + self.branch +
"\n \tBelted-bias: " + str(self.belted_bias) +
"\n \tOptimal pressure: " + str(self.opt_pressure))
class Engine:
def __init__(self, fuel_type, noise_level):
self.fuel_type = fuel_type
self.noise_level = noise_level
def __str__(self):
return ("Engine: \n \tFuel type: " + self.fuel_type +
"\n \tNoise level:" + str(self.noise_level))
class Body:
def __init__(self, size):
self.size = size
def __str__(self):
return "Body:\n \tSize: " + self.size
class Car:
def __init__(self, tyres, engine, body):
self.tyres = tyres
self.engine = engine
self.body = body
def __str__(self):
return str(self.tyres) + "\n" + str(self.engine) + "\n" + str(self.body)
Dynamic Extension
Sometimes it's necessary to model a concept that may be a subclass of another one, but it isn't
possible to know which class should be its superclass until runtime.
keyboard_arrow_down Example
Suppose we want to model a simple dog school that trains instructors too. It will be nice to re-
use Person and Student but students can be dogs or peoples. So we can remodel it this way:
class Dog:
def __init__(self, name, year_of_birth, breed):
self._name = name
self._year_of_birth = year_of_birth
self._breed = breed
def __str__(self):
return "%s is a %s born in %d." % (self._name, self._breed, self._year_of_birth)
class Student:
def __init__(self, anagraphic, student_id):
self._anagraphic = anagraphic
self._student_id = student_id
def __str__(self):
return str(self._anagraphic) + " Student ID: %d" % self._student_id
alec_student = Student("dsfs",1)
kudrjavka_student = Student(kudrjavka, 2)
print(alec_student)
print(kudrjavka_student)
# Lets look at another example of encapsulation
class BonusDistribution:
self.empId = employeeId
self.empRating = employeeRating
self.__bonusforRatingA = "70%" #making value private
self.__bonusforRatingB = "60%" #making value private
self.__bonusforRatingC = "50%" #making value private
self.__bonusforRatingD = "30%" #making value private
self.__bonusforRatingForRest = "No Bonus" #making value private
def bonusCalculator(self):
if self.empRating == 'A':
bonus = self.__bonusforRatingA
msg = "Bonus for this employee is :"+ bonus
return msg
elif self.empRating == 'B':
bonus = self.__bonusforRatingB
msg = "Bonus for this employee is :"+ bonus
return msg
elif self.empRating == 'C':
bonus = self.__bonusforRatingC
msg = "Bonus for this employee is :"+ bonus
return msg
elif self.empRating == 'D':
bonus = self.__bonusforRatingD
msg = "Bonus for this employee is :"+ bonus
return msg
else:
bonus = self.__bonusforRatingForRest
msg = "Bonus for this employee is :"+ bonus
return msg
emp1 = BonusDistribution(1232,'B')
emp2 = BonusDistribution(1342,'A')
emp3 = BonusDistribution(1031,'E')
emp2.bonusCalculator()
emp1.bonusCalculator()
emp3.bonusCalculator()
emp1._bonusforRatingB = "90%"
emp1.bonusCalculator()
To change the private attribute we need to define a function inside the class. Let's see how.
class BonusDistribution:
self.empId = employeeId
self.empRating = employeeRating
self.__bonusforRatingA = "70%" #making value private
self.__bonusforRatingB = "60%" #making value private
self.__bonusforRatingC = "50%" #making value private
self.__bonusforRatingD = "30%" #making value private
self.__bonusforRatingForRest = "No Bonus" #making value private
def bonusCalculator(self):
if self.empRating == 'A':
bonus = self.__bonusforRatingA
msg = "Bonus for this employee is :"+ bonus
return msg
elif self.empRating == 'B':
bonus = self.__bonusforRatingB
msg = "Bonus for this employee is :"+ bonus
return msg
elif self.empRating == 'C':
bonus = self.__bonusforRatingC
msg = "Bonus for this employee is :"+ bonus
return msg
elif self.empRating == 'D':
bonus = self.__bonusforRatingD
msg = "Bonus for this employee is :"+ bonus
return msg
else:
bonus = self.__bonusforRatingForRest
msg = "Bonus for this employee is :"+ bonus
return msg
def changeBonusForRatingForRest(self,value):
self.__bonusforRatingForRest = value
emp3 = BonusDistribution(1031,'E')
emp3.bonusCalculator()
emp3.changeBonusForRatingForRest("20%")
emp3.bonusCalculator()
We can see that the private attribute has now been changed and anyone can change that
attribute now. This is bad way of writing a method which can change an private attribute. Let's
make the function also private so it doesnot showup for everyone.
class BonusDistribution:
self.empId = employeeId
self.empRating = employeeRating
self.__bonusforRatingA = "70%" #making value private
self.__bonusforRatingB = "60%" #making value private
self.__bonusforRatingC = "50%" #making value private
self.__bonusforRatingD = "30%" #making value private
self.__bonusforRatingForRest = "No Bonus" #making value private
def bonusCalculator(self):
if self.empRating == 'A':
bonus = self.__bonusforRatingA
msg = "Bonus for this employee is :"+ bonus
return msg
elif self.empRating == 'B':
bonus = self.__bonusforRatingB
msg = "Bonus for this employee is :"+ bonus
return msg
elif self.empRating == 'C':
bonus = self.__bonusforRatingC
msg = "Bonus for this employee is :"+ bonus
return msg
elif self.empRating == 'D':
bonus = self.__bonusforRatingD
msg = "Bonus for this employee is :"+ bonus
return msg
else:
bonus = self.__bonusforRatingForRest
msg = "Bonus for this employee is :"+ bonus
return msg
def __changeBonusForRatingForRest(self,value):
self.__bonusforRatingForRest = value
emp3 = BonusDistribution(1031,'E')
emp3.bonusCalculator()
emp3
emp3.__changeBonusForRatingForRest("20%")
You can see that that method cannot be accessed now. Also, the method doesnot show up in
the class property:
If you know the name of the method, then you can still call the private member by using the
class name as shown below:
emp3._BonusDistribution__changeBonusForRatingForRest("20%")
emp3.bonusCalculator()
def __init__(self,a):
self.a =a
a1 = multiplyNum(2)
a2 = multiplyNum(3)
We are getting an error because by default multiply supports only numerical values.
We can change the function of multiply and this is what we call overloading.
class multiplyNum():
def __init__(self,a):
self.a =a
def __mul__(self,other):
return self.a*other.a
a1 = multiplyNum(2)
a2 = multiplyNum(3)
a1*a2
Great!! now we can multiply objects. We can also overide our mul function and get it do return
sum instead of multiplication.
class multiplyNum():
def __init__(self,a):
self.a =a
def __mul__(self,other):
a1 = multiplyNum(2)
a2 = multiplyNum(3)
a1*a2
def __init__(self,operator):
self.operator =operator
def __str__(self):
return "overloading the opearator :" + self.operator
print_ = printInformation('string')
print(print_)
print(summer(1, 1))
print(summer(["a", "b", "c"], ["d", "e"]))
print(summer("abra", "cadabra"))
# Polymorphism example
class Instagram:
def share_stories(self):
print("share your stories on Instagram!!!")
class Facebook:
def share_stories(self):
print("share your stories on Facebook!!!")
def ShareStory(application):
application.share_stories()
insta = Instagram()
fb = Facebook()
ShareStory(insta)
ShareStory(fb)
There is an Object Oriented Programming (OOP) principle called Single Responsibility Principle
(SRP) and it states: "A class should have one single responsibility" or "A class should have only
one reason to change".
If you come across a class which doesn't follow the SRP principle, you should spilt it. You will be
grateful to SRP during your software maintenance.
There is an excellent example from wikipedia which is nicely understood the concept of SRP:-
"Martin defines a responsibility as a reason to change, and concludes that a class or module
should have one, and only one, reason to be changed (e.g. rewritten). As an example, consider a
module that compiles and prints a report. Imagine such a module can be changed for two
reasons. First, the content of the report could change. Second, the format of the report could
change. These two things change for very different causes; one substantive, and one cosmetic.
The single-responsibility principle says that these two aspects of the problem are really two
separate responsibilities, and should, therefore, be in separate classes or modules. It would be a
bad design to couple two things that change for different reasons at different times.
The reason it is important to keep a class focused on a single concern is that it makes the class
more robust. Continuing with the foregoing example, if there is a change to the report
compilation process, there is a greater danger that the printing code will break if it is part of the
same class." Source- Wikipedia
Refer:- https://en.wikipedia.org/wiki/Single-
responsibility_principle#:~:text=The%20single%2Dresponsibility%20principle%20(
SRP,the%20class%2C%20module%20or%20function.
a=10
b = a
# Here the id is same for both a & b, hence if we want to change the value it will reflec
a = 20
print(a,b)
But many times we want to change the newly created values only, we dont want to modify the
original value so for that we can achieve this with creating copies in two ways:-
1) Deep Copy
2) Shallow Copy
a = ["ineuron","datascience"]
# Shallow copy can be created using copy module
import copy
a_new = copy.copy(a)
print(a)
print(a_new)
print(id(a))
print(id(a_new)) # Id will be different
print(a)
print(a_new)
a[1][1] = 'Update_value'
print(a)
print(new_a)
We can create deep copy using deepcopy() function present in copy module.
import copy
a = [1,2,3]
a_new = copy.deepcopy(a)
print(a)
print(a_new)
print(id(a))
print(id(a_new))
a[1]="change"
print(a)
print(a_new)
a = [[1,2,3],[4,5,6],[7,8,9]]
a_new = copy.deepcopy(a)
print(a)
print(a_new)
a[0][0] = "update"
print(a)
print(a_new) # Not changed this value
# Few more oops example
# Single inheritence
class Ineuron:
company_website = ""
name = ''
def contact_details(self):
print('Contact us at ', self.company_website)
class Datascience(Ineuron):
def __init__(self):
self.year_of_establishment= 2018
def est_details(self):
print('{0} Company was established in {1}'
.format(self.name,self.year_of_establishment))
ds = Datascience()
ds.est_details()
# Multiple inheritence
class OS:
multi_task = True
os_name = 'Windows OS'
windows = windows()
# Multilevel inheritence
class champ:
num_of_courses = 12
class Datascience(champ):
course_type = 'Data-Science'
class AI(Datascience):
def __init__(self):
self.company = "xyz"
print('The company {0} offers total {1} different types of courses. Most trending
AI = AI()
OOP allows us to combine the data and functionality and wrap it inside something which is
In programming any real world entity which has specific attributes or features can be
represented as an Object.
Along with attributes each object can take some actions also which are called it’s “behaviors”
In programming world, these attributes are called data members and behaviours/actions are
called methods
We also can show behaviors like walking, talking, running, eating etc
Now to create/represent objects we first have to write all their attributes and behavio
keyboard_arrow_down Class
A class is used to specify the basic structure of an object and it combines attributes an
Thus we can say that a class represents the data type and object represents a kind of var
For Example:- Each person collectively come under a class called Human Being. So we belo
image.png
image.png
class Emp:
pass
e=Emp()
print(type(e))
print(e)
id(e)
2. The second line shows the address of the object to which the reference e is pointing
3. The name __main__ is the name of the module which Python automatically allots to our f
Local Variables: Created locally inside a method and destroyed when the method execution
Class Variables: Created inside a class and shared by every object of that class. Sometim
class Emp:
def __init__(self):
print("Object created. . .")
print(id(self))
e=Emp()
print(id(e))
But while calling this method , Python also passes the address of the object , for which
Thus , when we define the __init__() method we must provide it atleast one formal argumen
e=Emp()
print(id(e))
print("Age:",e.age,"Name:",e.name,"Salary:",e.salary)
class Emp:
def __init__(self):
self.age=25
self.name="Rahul"
self.salary=30000
def show(self):
print(self.age,self.name,self.salary)
e=Emp()
f=Emp()
e.show()
f.show()
print("Age:",e.age,"Name:",e.name)
print("Age:",self.age,"Name:",self.name)
e.salary=30000.0
print("Age:",e.age,"Name:",e.name,e.salary)
class Emp:
def __init__(self,age,name,salary):
self.age=age
self.name=name
self.salary=salary
e=Emp(25,"Rahul",30000.0)
print("Age:",e.age,"Name:",e.name,"Salary:",e.salary)
f=Emp(31,"Varun",45000.0)
print("Age:",f.age,"Name:",f.name,"Salary:",f.salary)
class Emp:
def __init__(self,x,y,z):
self.age=x
self.name=y
self.salary=z
e=Emp(25,"Rahul",30000.0)
print("Age:",e.age,"Name:",e.name,"Salary:",e.salary)
f=Emp(31,"Varun",45000.0)
print("Age:",f.age,"Name:",f.name,"Salary:",f.salary)
class Emp:
def __init__(self,name):
self.name=name
def __init__(self,name,age):
self.name=name
self.age=age
def __init__(self,name,age,sal):
self.name=name
self.age=age
self.sal=sal
e1=Emp("amit")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-980ca38a04a0> in <module>
----> 1 e1=Emp("amit")
e2=Emp("sumit",23)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-f7e73b0e25c4> in <module>
----> 1 e2=Emp("sumit",23)
e3=Emp("deepak",34,50000.)
print(e1.name)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-8-0f3f6405404d> in <module>
----> 1 print(e1.name)
print(e2.name,e2.age)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-9-7e662a0a512c> in <module>
----> 1 print(e2.name,e2.age)
print(e3.name,e3.age,e3.sal)
deepak 34 50000.0
class Emp:
def __init__(self,name,age=0,sal=0.0):
self.name=name
self.age=age
self.sal=sal
e1=Emp("amit")
e2=Emp("sumit",23)
e3=Emp("deepak",34,50000.)
print(e1.name)
print(e2.name,e2.age)
print(e3.name,e3.age,e3.sal)
class Emp:
def __init__(self,*name):
self.name=name
#self.age=age
#self.sal=sal
#e1=Emp("amit")
#e2=Emp("sumit",23)
e3=Emp("deepak",34,50000.)
print(e3.name)
#print(e2.name,e2.age)
#print(e3.name,e3.age,e3.sal)
Types Of Methods
Instance methods are the most common type of methods in Python classes.
These are called instance methods because they can access instance members of the object
These methods always take atleast one parameter, which is normally called self, which po
Through the self parameter, instance methods can access data members and other methods on
This gives them a lot of power when it comes to modifying an object’s state.
class Emp:
def __init__(self,age,name,salary):
print(id(self))
self.age=age
self.name=name
self.salary=salary
def show(self):
print("Age:",self.age,"Name:",self.name,"Salary:",self.salary)
e=Emp(25,"Rahul",30000.0)
1471557586656
print(id(e))
1471557586656
f=Emp(31,"Varun",45000.0)
1471557586464
print(id(f))
1471557586464
e.show()
f.show()
class Emp:
def __init__(self,name,age,sal):
name=name
age=age
sal=sal
print(name,age,sal)
def show(self):
print(self.age,self.name,self.sal)
e1=Emp("amit",34,50000.0)
amit 34 50000.0
e1.show()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-28-322691ee7613> in <module>
----> 1 e1.show()
<ipython-input-26-c596fa30346d> in show(self)
5 sal=sal
6 def show(self):
----> 7 print(self.age,self.name,self.sal)
This attribute is automatically added by Python and it contains all the attributes defined for the
object itself.
class Emp:
def __init__(self):
self.name="Amit"
self.age=24
self.sal=50000.0
e1=Emp()
print(e1.__dict__)
class Emp:
def __init__(self):
self.name="Amit"
self.age=24
def set_sal(self):
self.sal=50000.0
e1=Emp()
print(e1.__dict__)
e1.set_sal()
print(e1.__dict__)
class Emp:
def __init__(self):
self.name="Amit"
self.age=24
self.sal=50000.0
def show(self):
print(self.name,self.age,self.sal,self.department)
e1=Emp()
print(e1.__dict__)
e1.__dict__['department']='IT'
print(e1.__dict__)
e1.show()
Amit 24 50000.0 IT
Since dict is a dictionary , we can manipulate it and add/del instance members from it
class Emp:
def __init__(self):
self.name="Amit"
self.age=24
self.sal=50000.0
def show(self):
print(self.name,self.age,self.sal,self.department)
e1=Emp()
print(e1.__dict__)
e1.__dict__['department']='IT'
print(e1.__dict__)
e1.show()
Amit 24 50000.0 IT
del e1.__dict__['age']
e1.show()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-56-322691ee7613> in <module>
----> 1 e1.show()
<ipython-input-49-e85b7c51a657> in show(self)
5 self.sal=50000.0
6 def show(self):
----> 7 print(self.name,self.age,self.sal,self.department)
print(e1.__dict__)
dic={}
dic2={'1':"sunny","2":"savita"}
dic.update(dic2)
dic
e1=Emp(name="Amit",age=24,sal=50000.0)
e1.show()
Amit 24 50000.0
Till now we can say there are 4 ways in Python to create instance variables:
class Emp:
def __init__(self,name,age,sal):
self.name=name
self.age=age
self.sal=sal
def setDept(self,department):
self.department=department
def setProject(self,project):
self.project=project
def setBonus(self,bonus):
self.bonus=bonus
def remove(self):
del self.name
del self.age
e1=Emp("Amit",24,30000.0)
e1.setDept("Finance")
e1.setProject("Banking Info System")
e1.setBonus(20000.0)
print(e1.__dict__)
{'name': 'Amit', 'age': 24, 'sal': 30000.0, 'department': 'Finance', 'project': 'Ban
e1.remove()
e1.__dict__
{'sal': 30000.0,
'department': 'Finance',
'project': 'Banking Info System',
'bonus': 20000.0}
del e1.sal
e1.__dict__
e2=Emp("Sumit",34,45000.0)
e2.setDept("Production")
print()
print(e2.__dict__)
Using del self .<var_name> from the body of any instance method within the class
They are also called as static variables , although there is no static keyword used with
The are shared by all instances of the class and have the same value for each instance of
In Python , for every class one special object is created called as class object
Rather , for every class , Python itself creates an object called as class object and ins
Inside classmethod using name of the class or using the special reference cls
Using name of the class anywhere inside the methods of the class
Special Note:We must never modify a class variable using self or object reference , becau
class CompStudent:
stream = 'cse'
def __init__(self,name,roll):
self.name = name
self.roll = roll
obj1 = CompStudent('Atul',1)
print(obj1.name)
print(obj1.roll)
print(obj1.stream)
Atul
1
cse
CompStudent.stream
'cse'
obj2 = CompStudent('Chetan', 2)
print(obj2.name)
print(obj2.roll)
print(obj2.stream)
Chetan
2
cse
print(CompStudent.stream)
cse
class CompStudent:
def __init__(self,name,roll):
CompStudent.stream='cse'
self.name = name
self.roll = roll
def test(self):
CompStudent.schoolname="adsnak"
obj1= CompStudent('Chetan', 2)
print(obj1.name)
print(obj1.roll)
print(obj1.stream)
Chetan
2
cse
obj1.test()
print(obj1.schoolname)
adsnak
print(obj2.name)
print(obj2.roll)
print(obj2.stream)
print(CompStudent.stream)
As we know , class variables are owned by a class itself (i.e., by its definition), so to
e1=Emp()
print(e1.__dict__)
print(Emp.__dict__)
class Sample:
i=10
def __init__(self):
Sample.j=20
def f1(self):
Sample.k=30
Sample.m=40
print(Sample.__dict__)
Why the code is showing only 2 class variables even though we have 4 ?
This is because the class variable k will only be created when f1() gets called . Similar
class Sample:
i=10
def __init__(self):
Sample.j=20
def f1(self):
Sample.k=30
Sample.m=40
s1=Sample()
print(Sample.__dict__)
class Sample:
i=10
def __init__(self):
Sample.j=20
def f1(self):
Sample.k=30
Sample.m=40
s1=Sample()
s2=Sample()
s1.f1()
s2.f1()
print(Sample.__dict__)
class Sample: i=10 def init(self): print("Constructor called. . .") print(Sample.i) print(self.i) def
f1(self): print("f1 called. . .") print(Sample.i) print(self.i)
Class Methods
Static Methods
Just like we can have class variables , similarly Python also allows us to create class methods.
These are those methods which work on the class as a whole , instead of working on it’s object.
For , example in our Emp class if we want to initialize the class variable raise_per inside a
method , then the best way would be to create a class method for this purpose
To create a class method we write the special word @classmethod on top of method definiti
Syntax:
class <class_name>:
@classmethod #decorator
def <method_name>(cls)
// class specific code
Notice that a class method gets a special object reference passed as argument by Python
called as class refercnce
To define a class method it is compulsory to use the decorator @classmethod
ClassMethods can only access class level data and not instance specific data
Just like Python passed self as argument to instance methods , it automatically passes cls as
argument to classmethods
The argument cls is always passed as the first argument and represents the class object.
Recall , that for every class Python creates a special object called class object , so the reference
cls points to this object.
The name cls is just a convention , although we can give any name to it.
Although we can use object reference also to call a classmethod but it is highly recommended
not to do so , since classmethods do not work upon individual instances of the class
Write a program to create a class called Emp , having an instance members called name , age
and sal . Also declare a class variable called raise_amount to store the increment percentage of
sal and set it the value given by the user Now provide following methods in your class
init_() : This method should initialize instance members with the parameter passed
increase_sal(): This method should calculate the increment in sal and add ot to the instance
member sal display(): This method should display name , age and sal of the employee Finally , in
the main script , create 2 Emp objects , initialize them and increase their salary . Finally display
the data
class Emp:
raise_amount=0
@classmethod
def set_raise_amount(cls):
print(id(cls))
cls.raise_amount=float(input("Enter raise percentage:"))##This can can also be wr
def __init__(self,name,age,sal):
print(id(self))
self.name=name
self.age=age
self.sal=sal
def increase_sal(self):
self.sal=self.sal+(self.sal*Emp.raise_amount/100)
def display(self):
print(self.name,self.age,self.sal)
Emp.set_raise_amount()
1759103895504
Enter raise percentage:10
e1=Emp("Amit",24,50000.0)
print(id(e1))
1759130971008
1759130971008
e1.display()
Amit 24 50000.0
e1.increase_sal()
e1.display()
Amit 24 55000.0
class Emp:
def __init__(self,name,age,sal):
print(id(self))
self.name=name
self.age=age
self.sal=sal
def increase_sal(self):
self.sal=self.sal+(self.sal*Emp.raise_amount/100)
def display(self):
print(self.name,self.age,self.sal)
Emp.set_raise_amount()
e1=Emp("Amit",24,50000.0)
1759114747040
e1.display()
Amit 24 50000.0
print(id(e1))
1759114747040
e2=Emp("Sunny",25,25000.0)
e2.display()
Sunny 25 25000.0
e2.increase_sal()
e2.display()
Sunny 25 27500.0
Emp.set_raise_amount()
e2.increase_sal()
e2.display()
Sunny 25 41250.0
Factory methods are those methods which return a class object (like constructor) for diff
It is similar to function overloading in C++ , but since, Python doesn't have anything as
To understand this , suppose we want to create a class called Date , which allows us to i
Obviously , we will have __init__() method , which will accept 3 parameters representing
class Date:
def __init__(self, day=10, month=10, year=2000):
self.day=day
self.month=month
self.year=year
def show(self):
print(self.day,self.month,self.year,sep="/")
d1=Date(10,12,2016)
d1.show()
Now suppose we also want to allow date arguments to be passed as a single string (“10-12
For this we can create a classmethod in the class which accepts a string representing dat
class Date:
def __init__(self, day=10, month=10, year=2000):
self.day=day
self.month=month
self.year=year
def show(self):
print(self.day,self.month,self.year,sep="/")
@classmethod
def from_string(cls, string_date):
day, month, year = map(int, string_date.split('-'))
myDate = cls(day, month, year)
return myDate
d1=Date(10,12,2016)
d1.show()
d2=Date.from_string("15-11-2017")
d2.show()
keyboard_arrow_down Static method
The third type of method a Python class can contain are called static methods
Static methods, much like class methods, are methods that are bound to a class rather than it’s
object.
Just like class methods , they also do not require any object to be called and can be called using
name of the class
The difference between a static method and a class method is: Static method knows nothing
about the class and just deals with the parameters.
Class method works with the class since it’s parameter is always the class itself.
This means that a static method doesn’t even get cls reference unlike a class method
It knows nothing about the class and is only interested to work upon it’s parameters
To create a static method we write the decorator @staticmethod on top of method definitio
Syntax:
class <class_name>:
@staticmethod
def <method_name>(<arg_list> )
pass ##argument specific code
#Notice that a static method doesn’t get any implicit argument by Python
class MyMath:
@staticmethod
def add_nos(a,b):
c=a+b
return c
@staticmethod
def mult_nos(a,b):