
On this article, we’ll dig into object-oriented programming (OOP) in Python. We gained’t go too deeply into the theoretical points of OOP. The principle purpose right here is to exhibit how we are able to use the object-oriented paradigm with Python.
In response to Statista, Python is the fourth most used programming language amongst builders. Why is that? Nicely, some say that it’s due to Python’s simplified syntax; others say it’s due to Python’s versatility. No matter the reason being, if we wish to research a trending programming language, Python ought to be one among our decisions.
Contents:
- The Fundamentals of OOP
- Lessons and Objects
- Defining a New Technique
- Entry Modifiers: Public, Protected and Personal
- Inheritance
- Polymorphism
- Technique Overloading
- Technique Overriding
The Fundamentals of OOP
Let’s begin with a delicate abstract of object-oriented programming. Object-oriented programming is a programming paradigm — a bunch of concepts that set a regular for the way issues should be completed.
The concept behind OOP is to mannequin a system utilizing objects. An object is a element of our system of curiosity, and it often has a particular goal and habits. Every object accommodates strategies and knowledge. Strategies are procedures that carry out actions on knowledge. Strategies would possibly require some parameters as arguments.
Java, C++, C#, Go, and Swift are all examples of object-oriented programming languages. The implementation of the OOP ideas in all of these languages is completely different, after all. Each language has its syntax, and on this article, we’ll see how Python implements the object-oriented paradigm.
To be taught extra about OOP typically, it’s price studying this text from MDN, or this fascinating dialogue about why OOP is so widespread.
Lessons and Objects
The primary essential idea of OOP is the definition of an object. Let’s say you've got two canines, referred to as Max and Pax. What have they got in widespread? They're canines and so they signify the thought of a canine. Even when they're of a unique breed or coloration, they nonetheless are canines. On this instance, we are able to mannequin Max and Pax as objects or, in different phrases, as situations of a canine.
However wait, what's a canine? How can I mannequin the thought of a canine? Utilizing lessons.
As we are able to see within the image above, a category is a template that defines the info and the habits. Then, ranging from the template supplied by the category, we create the objects. Objects are situations of the category.
Let’s take a look at this Python code:
class Canine():
def __init__(self, identify, breed):
self.identify = identify
self.breed = breed
def __repr__(self):
return f"Canine(identify={self.identify}, breed={self.breed})"
max = Canine("Max", "Golden Retriever")
pax = Canine("Pax", "Labrador")
print(max)
print(pax)
On line 1, we declare a brand new class utilizing the identify Canine
. Then we stumble upon a way referred to as __init__
. Each Python class has this, as a result of it’s the default constructor. This methodology is used to initialize the item’s state, so it assigns values to the variables of the newly created object. As arguments of the constructor, we've the identify
, the breed
, and a particular key phrase referred to as self
. It’s not a coincidence that that is the primary argument of the strategy.
Inside the category code, the self
key phrase represents the present occasion of the category. Because of this every time we wish to entry a sure methodology or variable that belongs to an occasion of the category (max
or pax
are two completely different situations), we should use the self
key phrase. Don’t fear if it’s not fully clear now; it will likely be within the subsequent sections.
Have a look at the primary line of the __init__
methodology — self.identify = identify
. In phrases, this says to the Python interpreter: “Okay, this object that we’re creating can have a reputation (self.identify
), and this identify is contained in the identify
argument”. The identical factor occurs for the breed
argument. Okay, so we might have stopped right here. That is the essential blueprint used to outline a category. Earlier than leaping to the execution of this snippet, let’s take a look at the strategy that was added after the __init__
.
The second methodology is named __repr__
. In Python, the __repr__
methodology represents the category object as a string. Often, if we don’t explicitly outline it, Python implements it in its personal manner, and we’ll now see the distinction. By default, if we don’t explicitly outline a __repr__
methodology, when calling the perform print()
or str()
, Python will return the reminiscence pointer of the item. Not fairly human-readable. As a substitute, if we outline a customized __repr__
methodology, we've a pleasant model of our object in a stringed vogue, which may also be used to assemble the item once more.
Let’s make a change to the code above:
class Canine:
def __init__(self, identify, breed):
self.identify = identify
self.breed = breed
max = Canine("Max", "Golden Retriever")
pax = Canine("Max", "Golden Retriever")
print(max)
print(pax)
print(max == pax)
If we save and run this code, that is what we get:
<__main__.Canine object at 0x0000026BD792CF08>
<__main__.Canine object at 0x0000026BD792CFC8>
False
Wait, how can or not it's potential that they aren’t two equal canines, if they've the identical identify and the identical breed? Let’s visualize it utilizing the diagram we made earlier than.
First, once we execute print(max)
, Python will see that there’s no customized definition of a __repr__
methodology, and it'll use the default implementation of the __repr__
methodology. The 2 objects, max
and pax
, are two completely different objects. Sure, they've the identical identify and the identical breed, however they’re completely different situations of the category Canine
. In actual fact, they level to completely different reminiscence areas, as we are able to see from the primary two strains of the output. This truth is essential for understanding the distinction between an object and a category.
If we now execute the primary code instance, we are able to see the distinction within the output once we implement a customized __repr__
methodology:
Canine(identify=Max, breed=Golden Retriever)
Canine(identify=Pax, breed=Labrador)
Defining a New Technique
Let’s say we wish to get the identify of the max
object. Since on this case the identify
attribute is public, we are able to merely get it by accessing the attribute utilizing max.identify
. However what if we wish to return a nickname for the item?
Nicely, in that case, we create a way referred to as get_nickname()
inside our class. Then, outdoors the definition of the category, we merely name the strategy with max.get_nickname()
:
class Canine:
def __init__(self, identify, breed):
self.identify = identify
self.breed = breed
def get_nickname(self):
return f"{self.identify}, the {self.breed}"
def __repr__(self):
return f"Canine(identify={self.identify}, breed={self.breed})"
max = Canine("Max", "Golden Retriever")
pax = Canine("Pax", "Labrador")
print(max.identify)
print(max.get_nickname())
If we run this snippet, we get the next output:
> python snippet.py
Max
Max, the Golden Retriever
Entry Modifiers: Public, Protected and Personal
Let’s now contemplate entry modifiers. In OOP languages, entry modifiers are key phrases used to set the accessibility of lessons, strategies or attributes. It’s a unique scenario in C++ and Java, the place entry modifiers are express key phrases outlined by the language. In Python, there’s no such factor. Entry modifiers in Python are a conference fairly than a assure over entry management.
Let’s take a look at what this implies with a code pattern:
class BankAccount:
def __init__(self, quantity, openingDate):
self.quantity = quantity
self._openingDate = openingDate
self.__deposit = 0
On this snippet, we create a category referred to as BankAccount
. Any new BankAccount
object should have three attributes: a quantity, a gap date and an preliminary deposit set to 0. Discover the one underscore (_
) earlier than openingDate
and the double underscore (__
) earlier than deposit
.
Nice! In response to Python’s conference, the one underscore is used as a prefix for protected
members, whereas the double underscore is for personal
members. What does this imply in observe? Let’s attempt to add the code under below the category definition:
account = BankAccount("ABXX", "01/01/2022")
print(account.quantity)
print(account._openingDate)
print(account.__deposit)
If we attempt to execute this code, we’ll get one thing like this:
> python snippet.py
ABXX
01/01/2022
Traceback (most up-to-date name final):
File "snippet.py", line 14, in <module>
print(account.__deposit)
AttributeError: 'BankAccount' object has no attribute '__deposit'
We are able to print the account quantity
as a result of it’s a public attribute. We are able to print the openingDate
, even when, in keeping with the conference, it’s not suggested. We are able to’t print the deposit
.
Within the case of the deposit attribute, the right technique to learn or modify its worth ought to be by get()
and set()
strategies. Let’s see an instance of this:
class BankAccount:
def __init__(self, quantity, openingDate):
self.quantity = quantity
self._openingDate = openingDate
self.__deposit = 0
def getDeposit(self):
return self.__deposit
def setDeposit(self, deposit):
self.__deposit = deposit
return True
account = BankAccount("ABXX", "01/01/2022")
print(account.getDeposit())
print(account.setDeposit(100))
print(account.getDeposit())
Within the code above, we outline two new strategies. The primary one is named getDeposit
, and the second is setDeposit
. As their names suggest, they’re used to get or set the deposit. It’s a conference in OOP to create get and set strategies for all the attributes that must be learn or modified. So, as an alternative of instantly accessing them from outdoors the category, we implement strategies to try this.
As we are able to simply guess, executing this code provides the next output:
> python snippet.py
0
True
100
Inheritance
DRY. Don’t repeat your self. Object-oriented programming encourages the DRY precept, and inheritance is without doubt one of the methods used to implement the DRY precept. On this part, we’ll see how inheritance works in Python. Please notice that we’ll use the phrases mum or dad class and baby class. Different aliases would possibly embody base class for the mum or dad and derived class for the youngsters. Since inheritance defines a hierarchy of lessons, it’s fairly handy to distinguish between the mum or dad and all the youngsters.
Okay, so let’s begin with an instance. Let’s say we wish to mannequin a classroom. A classroom is made by a professor and various college students. What do all of them have in widespread? What relationship do all of them share? Nicely, they’re definitely all people. As such, they share a sure variety of options. For simplicity right here, we outline a category Individual
as having two personal attributes, identify and surname. This class additionally accommodates the get()
and set()
strategies.
The picture under reveals a mum or dad class and two youngsters.
As we are able to see, in each Pupil
and Professor
lessons we've all of the strategies and attributes outlined for the Individual
class, as a result of they inherit them from Individual
. Moreover, there are different attributes and strategies highlighted in daring which might be particular to the kid class.
Right here’s the code for this instance:
class Individual:
def __init__(self, identify, surname):
self.__name = identify
self.__surname = surname
def getName(self):
return self.__name
def getSurname(self):
return self.__surname
def setName(self, newName):
self.__name = newName
def setSurname(self, newSurname):
self.__surname = newSurname
Then, we've two entities to mannequin, the Pupil
and the Professor
. There’s no must outline all of the issues we outline above within the Individual
class for Pupil
and Professor
additionally. Python permits us to make the Pupil
and the Professor
class inherit a bunch of options from the Individual
class (mum or dad).
Right here’s how we are able to try this:
class Pupil(Individual):
def __init__(self, identify, surname, grade):
tremendous().__init__(identify, surname)
self.__grade = grade
def getGrade(self):
return self.__grade
def setGrade(self, newGrade):
self.__grade = newGrade
Within the first line, we outline a category utilizing the same old class Pupil()
syntax, however contained in the parentheses we put Individual
. This tells the Python interpreter that this can be a new class referred to as Pupil
that inherits attributes and strategies from a mum or dad class referred to as Individual
. To distinguish this class a bit, there’s a further attribute referred to as grade
. This attribute represents the grade the coed is attending.
The identical factor occurs for the Professor
class:
class Professor(Individual):
def __init__(self, identify, surname, teachings):
tremendous().__init__(identify,surname)
self.__teachings = teachings
def getTeachings(self):
return self.__teachings
def setTeachings(self, newTeachings):
self.__teachings = newTeachings
There’s a brand new aspect we haven’t seen earlier than. On line 3 of the snippet above, there’s an odd perform referred to as tremendous().__init__(identify,surname)
.
The tremendous()
perform in Python is used to present the kid entry to members of a mum or dad class. On this case, we’re calling the __init__
methodology of the category Individual
.
Polymorphism
The instance launched above reveals a strong thought. Objects can inherit behaviors and knowledge from different objects of their hierarchy. The Pupil
and Professor
lessons have been each subclasses of the Individual
class. The concept of polymorphism, because the phrase says, is to permit objects to have many shapes. Polymorphism is a sample utilized in OOP languages during which lessons have completely different functionalities whereas sharing the identical interface.
Talking of the instance above, if we are saying {that a} Individual
object can have many shapes, we imply that it may be a Pupil
, a Professor
or no matter class we create as a subclass of Individual
.
Let’s see another fascinating issues about polymorphism:
class Car:
def __init__(self, model, coloration):
self.model = model
self.coloration = coloration
def __repr__(self):
return f"{self.__class__.__name__}(model={self.model}, coloration={self.coloration})"
class Automotive(Car):
cross
tractor = Car("John Deere", "inexperienced")
red_ferrari = Automotive("Ferrari", "pink")
print(tractor)
print(red_ferrari)
So, let’s take a look. We outline a category Car
. Then, we create one other class referred to as Automotive
as a subclass of Car
. Nothing new right here. To check this code, we create two completely different objects and retailer them in two separate variables referred to as tractor
and red_ferrari
. Be aware right here that the category Automotive
doesn’t have something inside. It’s simply outlined as a unique class, however until now it has had no completely different habits from its mum or dad. Don’t hassle about what’s contained in the __repr__
methodology for now, as we’ll come again to it later.
Are you able to guess the output of this code snippet? Nicely, the output is the next:
Car(model=John Deere, coloration=inexperienced)
Automotive(model=Ferrari, coloration=pink)
Be aware the magic occurring right here. The __repr__
methodology is outlined contained in the Car
class. Any occasion of Automotive
will undertake it, since Automotive
is a subclass of Car
. However Automotive
doesn’t outline a customized implementation of __repr__
. It’s the identical as its mum or dad.
So the query right here is why the habits is completely different. Why does the print present two various things?
The reason being that, at runtime, the Python interpreter acknowledges that the category of red_ferrari
is Automotive
. self.__class__.__name__
will give the identify of the category of an object, which on this case is the self
object. However keep in mind, we've two completely different objects right here, created from two completely different lessons.
If we wish to verify whether or not an object is an occasion of a sure class, we might use the next features:
print(isinstance(tractor, Car))
print(isinstance(tractor, Automotive))
On the primary line, we’re asking the next query: is tractor
an occasion of the category Car
?
On the second line, we’re as an alternative asking: is tractor
an occasion of the category Automotive
?
Technique Overloading
In Python, like in another OOP language, we are able to name the identical methodology in several methods — for instance, with a unique variety of parameters. That is perhaps helpful once we wish to design a default habits however don’t wish to forestall the person from customizing it.
Let’s see an instance:
class Overloading:
def sayHello(self, i=1):
for occasions in vary(i):
print("Good to satisfy you!")
a = Overloading()
print("Working a.sayHello():")
a.sayHello()
print("Working a.sayHello(5):")
a.sayHello(5)
Right here, we outline a way referred to as sayHello
. This methodology has just one argument, which is i
. By default, i
has a worth of 1. Within the code above, once we name a.sayHello
for the primary time with out passing any argument, i
will assume its default worth. The second time, we as an alternative cross 5 as a parameter. This implies i=5
.
What's the anticipated habits then? That is the anticipated output:
> python snippet.py
Working a.sayHello():
Good to satisfy you!
Working a.sayHello(5):
Good to satisfy you!
Good to satisfy you!
Good to satisfy you!
Good to satisfy you!
Good to satisfy you!
The primary name to a.sayHello()
will print the message "Good to satisfy you!"
solely as soon as. The second name to a.sayHello()
will print "Good to satisfy you!"
5 occasions.
Technique Overriding
Technique overriding occurs when we've a way with the identical identify outlined each within the mum or dad and within the baby class. On this case, we are saying that the kid is doing methodology overriding.
Principally, it may be demonstrated as proven under. The next diagram reveals a toddler class overriding a way.
The sayHello()
methodology in Pupil
is overriding the sayHello()
methodology of the mum or dad class.
To point out this concept in observe, we are able to modify a bit the snippet we launched at first of this text:
class Individual:
def __init__(self, identify, surname):
self.identify = identify
self.surname = surname
def sayHello(self):
return ("Hey, my identify is {} and I'm an individual".format(self.identify))
class Pupil(Individual):
def __init__(self, identify, surname, grade):
tremendous().__init__(identify,surname)
self.grade = grade
def sayHello(self):
return ("Hey, my identify is {} and I'm a pupil".format(self.identify))
a = Individual("john", "doe")
b = Pupil("joseph", "doe", "eighth")
print(a.sayHello())
print(b.sayHello())
On this instance, we've the strategy sayHello()
, which is outlined in each lessons. The Pupil
implementation of sayHello()
is completely different, although, as a result of the coed says good day in one other manner. This strategy is versatile, as a result of the mum or dad is exposing not solely an interface but in addition a type of the default habits of sayHello
, whereas nonetheless permitting the youngsters to switch it in keeping with their wants.
If we run the code above, that is the output we get:
> python snippet.py
Hey, my identify is john and I'm an individual
Hey, my identify is joseph and I'm a pupil
Conclusion
By now, the fundamentals of OOP in Python ought to be fairly clear. On this article, we noticed the right way to create lessons and the right way to instantiate them. We addressed the right way to create attributes and strategies with completely different visibility standards. We additionally found basic properties of OOP languages like inheritance and polymorphism, and most significantly the right way to use them in Python.