OOP In Python
- OOP (Object Oriented Programming) in Python allows us to use objects to represent entities.
- Each object has its own attributes and methods derived from the Class it belongs to.
- Some examples of pre-existing classes in Python are Integer, Strings, Lists, Sets, Dictionary, etc.
- Whenever we are creating, for example, a new list in Python, we are actually creating an object of class List.
- The new list created has all the methods that are defined in List Class, for example, append, insert, sort, etc.
- We can create our own classes and objects in Python to build real-life applications
- Using OOPS allows us to organize, maintain and scale our code more efficiently.
- The four main pillars of OOP are Encapsulation, Inheritance, Abstraction, and Polymorphism.
Class and Objects
- An object is to represent an entity belonging to a particular Class.
- A class is a blueprint for creating objects in OOP.
- Classes have their own attributes and methods(functions) that can be accessed by objects belonging to that class.
- We can initialize the attributes of Class using init method (constructor).
class Dog: # class naming with PascalCase
# constructor
def __init__(self, name, age):
self.name = name # creating class attributes
self.age = age
def speak(self): # creating class methods
return f'My name is {self.name}, and I am {self.age} years old.'
bruno = Dog("Bruno", 9) # creating object
print(bruno.name) # accessing Class attribute from object
print(bruno.age)
print(bruno.speak()) # accessing Class method from object
Output:
- Bruno
- 9
- My name is Bruno, and I am 9 years old.
1. Encapsulation:
- Encapsulation allows us to hide the object's attributes and method from outside access.
- Python doesn't have encapsulation in the same sense as other OOP languages.
- Python doesn't have truly private or protected attributes and methods.
- Attributes and methods that are meant to be protected are prefixed with a single underscore: _
- However, they can still be accessed and modified using their names.
- Attributes and methods that are meant to be private are prefixed with a double underscore: __
- However, they can still be accessed and modified using name mangling.
- Also, both can still be accessed and modified using get and set methods.
class Dog:
def __init__(self, name, age):
self.__name = name # private attribute
self._age = age # protected attribute
def get_name(self): # get method
return self.__name
def set_name(self, name): # set method
self.__name = name
def get_age(self): # get method
return self._age
def set_age(self, age): # set method
self._age = age
def __show(self): # private method
return f"My name is {self.__name} and I am {self._age} years old."
def _speak(self): # protected method
return "WOOF!"
bruno = Dog("Bruno", 9)
bruno._Dog__name = "Shero" # modifying private attribute using name mangling
print(bruno._Dog__name) # accessing private attribute using name mangling
bruno.set_name("Cheems") # modifying private attribute using set method
print(bruno.get_name()) # accessing private attribute using get method
bruno._age = 6 # modifying protected attribute using its name
print(bruno._age) # accessing protected attribute using its name
bruno.set_age(3) # modifying protected attribute using set method
print(bruno.get_age()) # accessing protected attribute using get method
print(bruno._Dog__show()) # accessing private method using name mangling
print(bruno._speak()) # accessing protected method using it's name
Output:
- Shero
- Cheems
- 6
- 3
- My name is Cheems and I am 3 years old.
- WOOF!
2. Inheritance
- Inheritance allows us to create a new class (called 'child class') based on another class (called 'parent class').
- The child class inherits all the attributes and methods of the parent class.
- In addition, the child class it's own unique attributes and methods.
- There are several types of inheritance namely, single-level, multi-level, multiple, hierarchical, and hybrid.
class Pet:
def __init__(self, name, age):
self.name = name
self.age = age
def show(self):
print(f'My name is {self.name}, and I am {self.age} years old.')
class Cat(Pet): # inheriting parent class in child class
def speak(self): # accessing parent class attribute in child class by default
print("Meow!")
class Dog(Pet): # inheriting parent class in child class
def __init__(self, name, age, sport):
super().__init__(name, age) # accessing parent class attribute in child class using super
self.sport = sport # child class accessing its own attributes
def speak(self):
print(f'My favorite sport is {self.sport}.')
D = Dog("Bruno", 9, 'Football')
C = Cat("Missa", 6)
D.show() # accessing parent class method in child class
D.speak() # child class accessing its own attributes
C.show() # accessing parent class method in child class
C.speak() # child class accessing its own attributes
Output:
- My name is Bruno, and I am 9 years old.
- My favorite sport is Football.
- My name is Missa, and I am 6 years old.
- Meow!
3. Abstraction
- Abstraction allows us to hide classes and methods not necessary for the user.
- We can make an abstract class in python by setting the parent class of any class to ABC after importing it from abc (Abstract Class Bases).
- After that, we can create an abstract method using the @abstractmethod decorator.
- We can still access the non-abstract method of abstract classes.
from abc import ABC, abstractmethod
class PET(ABC): # create an abstract class
@abstractmethod # create an abstract method
def speak(self):
pass
def show(self):
print("I am a Pet.")
class Dog(PET):
def speak(self):
print("WOOF!")
class Cat(PET):
def speak(self):
print("Meow!")
bruno = Dog()
bruno.speak() # ignoring abstract method of abstract class
bruno.show() # non-abstract method of the abstract class still works
missa = Cat()
missa.speak() # ignoring abstract method of abstract class
missa.show() # non-abstract method of the abstract class still works
nemo = PET() # will throw an error because we cannot instantiate an abstract class.
Output:
- WOOF!
- I am a Pet.
- Meow!
- I am a Pet.
- Can't instantiate abstract class PET with abstract method speak
4. Polymorphism
- Polymorphism allows an object to take on many forms.
- Polymorphism in Python can be achieved in two ways: method over-riding and duck-typing.
A. Method Over-riding
Method overriding allows a child class to have a different definition of a method already defined in the parent class.
class Pet:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
print(f'My name is {self.name} and I am {self.age} years old.')
class Fish(Pet):
pass
class Dog(Pet):
def speak(self): # over-riding parent's class method
print(f'WOOF! My name is {self.name} and I am {self.age} years old.')
nemo = Fish("Nemo", 2)
bruno = Dog("Bruno", 9)
nemo.speak() # parent's class method runs
bruno.speak() # child's class method runs
Output:
- My name is Nemo and I am 2 years old.
- WOOF! My name is Bruno and I am 9 years old.
B. Duck Typing
Duck Typing allows different types of classes to have the same method name with its own definition.
class Fish:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self): # method of first class
print(f'My name is {self.name} and I am {self.age} years old.')
class Dog:
def __init__(self, name, age, breed):
self.name = name
self.age = age
self.breed = breed
def speak(self): # method of second class with same name
print(f'WOOF! My name is {self.name} and I am {self.age} years old, and I am a {self.breed}.')
nemo = Fish("Nemo", 2)
bruno = Dog("Bruno", 9, "Shiba Inu")
nemo.speak() # method of first class runs
bruno.speak() # method of the second class with the same name runs
Output:
- My name is Nemo and I am 2 years old.
- WOOF! My name is Bruno and I am 9 years old, and I am a Shiba Inu.
Dunder/Magic Methods:
- Dunder or magic methods are special methods used in Python classes.
- They are used to define how objects of classes behave in case they are used with in-built Python operations.
- Some commonly used dunder methods are init(self,...), str(self), repr(self), len(self), ads(self), del(self), etc.
- Dunder methods cannot be called, they run automatically when called by in-built python functions.
class SimpleClass:
# constructor
def __init__(self, value1, value2):
self.value1 = value1
self.value2 = value2
# string representation of the object
def __str__(self):
return f'value 1 is {self.value1} and value 2 is {self.value2}.'
# string representation for debugging purposes
def __repr__(self):
return f'This is an object of the class SimpleClass'
# returns output when len() function is called
def __len__(self):
return 1
# returns the sum when + operator is called between two objects.
def __add__(self, other):
return (self.value1 + other.value1, self.value2 + other.value2)
def __del__(self):
print("object deleted")
a = SimpleClass(5, 10) # __init__ is called
b = SimpleClass(7, 9) # __init__ is called
print(a) # __str__ is called
print(repr(a)) # __repr__ is called
print(len(a)) # __len__ is called
print(a + b) # __add_ is called
Output:
- value 1 is 5 and value 2 is 10.
- This is an object of the class SimpleClass
- 1
- (12, 19)
Top comments (0)