If we call a language a high level language, then it becomes necessary for that language to have object oriented paradigm. A developer should be able to write clean object oriented code. Moreover, no big projects can be build without using object orientation.
Python provides very clean object oriented design syntax. With the help of certain keywords, we can program the code by visualizing it as real world object. This chapter will not cover all object oriented programming concepts. However, it will teach you how to design an object oriented program in Python. So, lets get started.
Classes in Python
A class is a fundamental concept of OOP. You can think of it as a template for a real world object. For example in a sentence - Alice is a human, human is a class of the real world object Alice. Classes provides a way to bind data and functions together in a box. It can be seen as a template of a real world object. In technical terms, you can call it is a user defined datatype.
In Python, we can create a class like this.
class ClassName:
<statement-1>
.
.
.
<statement-N>
where statements can have variable statements and function statements. Lets see an example to understand better
class House:
def getDimensions(self):
return f"Height - {self.height}, Length - {self.length}, Width - {self.width}"
In the above example, we created a class named - House. We have declared three variable length, width and height. We have declared a function called getDimensions(self).
About self keyword: The self keyword defines or points to the object on which the method is being called.
An object is an instance of a Class.
Now, lets create an object of the class House. In the below example myHouse is an instance of class House. This means the self is now equal to myHouse. We have initialized variable of myHouse and called getDimensions method on myHouse. When the method getDimensions is getting executed the statement self.height will be equal to myHouse.height because self if pointing to myHouse.
myHouse = House()
myHouse.height = 100
myHouse.width = 500
myHouse.length = 800
dim = myHouse.getDimensions()
print(dim)
# Output -> Height - 100, Length - 800, Width - 500
Class constructor
A class constructor is used to construct a class by initializing its member and doing some initial setup required before accessing the class. Python provides a special method __init__(..) to initialize or construct a class.
Lets modify our class to add a constructor.
class House:
def __init__(self, length, width, height):
self.length = length
self.width = width
self.height = height
def getDimensions(self):
return f"Height - {self.height}, Length - {self.length}, Width - {self.width}"
myHouse = House(100, 500, 800)
dim = myHouse.getDimensions()
print(dim)
#Output -> Height - 800, Length - 100, Width - 500
In the code above, we are declaring instance variables inside the constructor and while object construction we are passing the individual values.
Class destructor
A class destructor is a special functional which gets called when the an instance of a class remains no more. This is used to clean up any variables or resources before an instance gets destroyed. Lets see this by adding a destructor to our House class.
class House:
def __init__(self, length, width, height):
self.length = length
self.width = width
self.height = height
def getDimensions(self):
return f"Height - {self.height}, Length - {self.length}, Width - {self.width}"
def __del__(self):
print("Destructor called")
myHouse = House(100, 500, 800)
dim = myHouse.getDimensions()
print(dim)
# Output -> Height - 800, Length - 100, Width - 500
del myHouse
# Output -> Destructor called
In the above example, we have deleted the instance myHouse and hence the destructor gets called.
Class variables v/s instance variables
A class variable is associated with a class and is shared across all instances.
An instance variable is associated with instance only.
Lets consider the below example
class Shooting:
remainingBullets = 100
def __init__(self, playerName):
self.playerName = playerName
self.remainingBullets = 10
Shooting.remainingBullets -= 10
def shoot(self):
if(Shooting.remainingBullets == 0):
print("Can not shoot. No more bullets")
else:
self.remainingBullets -=1
shooting_alice = Shooting("Alice")
print("\nGame created for Alice")
print(f"Remaining bullets [Total] - {Shooting.remainingBullets}")
print(f"Remaining bullets [Alice] - {shooting_alice.remainingBullets}")
shooting_bob = Shooting("Bob")
print("\nGame created for Bob")
print(f"Remaining bullets [Total] - {Shooting.remainingBullets}")
print(f"Remaining bullets [Bob] - {shooting_bob.remainingBullets}")
shooting_alice.shoot()
print("\nAlice fired a shot")
print(f"Remaining bullets [Total] - {Shooting.remainingBullets}")
print(f"Remaining bullets [Alice] - {shooting_alice.remainingBullets}")
shooting_bob.shoot()
print("\nBob fired a shot")
print(f"Remaining bullets [Total] - {Shooting.remainingBullets}")
print(f"Remaining bullets [Bob] - {shooting_bob.remainingBullets}")
shooting_chris = Shooting("Chris")
print("\nGame created for Chris")
print(f"Remaining bullets [Total] - {Shooting.remainingBullets}")
print(f"Remaining bullets [Chris] - {shooting_chris.remainingBullets}")
for i in range(0,5):
shooting_chris.shoot()
print("\nChris fired 5 shots")
print(f"Remaining bullets [Total] - {Shooting.remainingBullets}")
print(f"Remaining bullets [Chris] - {shooting_chris.remainingBullets}")
# Output ->
# Game created for Alice
# Remaining bullets [Total] - 90
# Remaining bullets [Alice] - 10
#
# Game created for Bob
# Remaining bullets [Total] - 80
# Remaining bullets [Bob] - 10
#
# Alice fired a shot
# Remaining bullets [Total] - 80
# Remaining bullets [Alice] - 9
#
# Bob fired a shot
# Remaining bullets [Total] - 80
# Remaining bullets [Bob] - 9
#
# Game created for Chris
# Remaining bullets [Total] - 70
# Remaining bullets [Chris] - 10
#
# Chris fired 5 shots
# Remaining bullets [Total] - 70
# Remaining bullets [Chris] - 5
Lets understand this with the example above. In the above example, there is one class variable remainingBullets and one instance variable remainingBullets. When an instance of the class Shooting is created, 10 bullets from Shooting class are allocated to the player. So the remaining bullets for class is now 10 less than what it was and the remaining bullets for the player is now 10.
When a player fires a shot, bullet count is decreased from the player's (instance) remainingBullets variable.
And when a player's game gets created, bullet count is decreased from the Shooting game's (class) remainingBullets variable.
The conclusion is, the class's variable can be shared and changed by different instances however the instance's variable can be changed by instance only.
Python jupyter notebook for code examples
Access Jupyter notebook for this chapter here:
Jupyter Notebook - OOP classes and instances