OOP - Classes and Objects
A class is a blueprint for creating objects.
| class Car:
pass
audi = Car()
bmw = Car()
print(type(audi))
print(audi)
print(type(bmw))
print(bmw)
|
<__main__.Car object at 0x7f0b7c1db920>
<__main__.Car object at 0x7f0b7c1dba10>
| class Dog:
def __init__(self, name, age): # Constructor
self.name = name
self.age = age
def bark(self):
return f"{self.name} says Woof!"
dog1 = Dog("Buddy", 3)
dog1.bark()
|
'Buddy says Woof!'
Inheritance
Single Inheritance
| class Car:
"""Superclass for all cars."""
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def start(self):
return f"{self.year} {self.make} {self.model} is starting."
def stop(self):
return f"{self.year} {self.make} {self.model} is stopping."
car1 = Car("Toyota", "Corolla", 2020)
car1.start()
|
'2020 Toyota Corolla is starting.'
| class Tesla(Car):
def __init__(self, model, year, battery_size=75):
super().__init__("Tesla", model, year)
self.battery_size = battery_size
def charge(self):
return f"{self.year} {self.make} {self.model} is charging with a {self.battery_size}-kWh battery."
tesla1 = Tesla("Model 3", 2021)
tesla1.start()
tesla1.charge()
|
'2021 Tesla Model 3 is charging with a 75-kWh battery.'
Multiple Inheritance
| class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} makes a sound."
class Pet:
def __init__(self, owner):
self.owner = owner
def get_owner(self):
return f"{self.owner} is the owner of this pet."
class Dog(Animal, Pet):
def __init__(self, name, owner):
Animal.__init__(self, name)
Pet.__init__(self, owner)
def speak(self):
return f"{self.name} says Woof!"
dog1 = Dog("Buddy", "Alice")
print(dog1.speak())
dog1.get_owner()
|
Buddy says Woof!
'Alice is the owner of this pet.'
Polymorphism
It provides a way to perform a single action in different forms
Method overriding allows a child class to provide a specific implementation of a method that is already defined in its parent class
| class Animal:
def speak(self):
return "Animal sound"
class Dog(Animal):
def speak(self):
return "Dog barks"
class Cat(Animal):
def speak(self):
return "Cat meows"
def animal_sound(animal):
"""Polymorphic function to get the sound of an animal."""
return animal.speak()
dog = Dog()
cat = Cat()
print(animal_sound(dog))
print(animal_sound(cat))
|
Dog barks
Cat meows
| class Shape:
def area(self):
raise NotImplementedError("Subclasses must implement this method")
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * (self.radius**2)
def print_area(shape):
print(f"The area is: {shape.area()}")
rectangle = Rectangle(5, 10)
circle = Circle(7)
print_area(rectangle)
print_area(circle)
|
The area is: 50
The area is: 153.86
| from abc import ABC, abstractmethod
class Vehicle(ABC):
@abstractmethod
def start(self):
pass
@abstractmethod
def stop(self):
pass
class Car(Vehicle):
def start(self):
return "Car is starting."
def stop(self):
return "Car is stopping."
class motorcycle(Vehicle):
def start(self):
return "Motorcycle is starting."
def stop(self):
return "Motorcycle is stopping."
def start_vehicle(vehicle):
print(vehicle.start())
def stop_vehicle(vehicle):
print(vehicle.stop())
car = Car()
motorcycle = motorcycle()
start_vehicle(car)
start_vehicle(motorcycle)
stop_vehicle(car)
stop_vehicle(motorcycle)
|
Car is starting.
Motorcycle is starting.
Car is stopping.
Motorcycle is stopping.
Encapsulation
Encapsulation involves bundling data and methods that operate on the data within a single unit
Public, private and protected or access modifiers
- Public: accessible by every other piece of code
- Protected: accessible by derived classes
- Private: accessible only by the objects itself
| class Person:
def __init__(self, name, age):
self.name = name # Public attribute
self._age = age # Protected attribute
self.__ssn = "123-45-6789" # Private attribute
def get_ssn(self):
return self.__ssn # Method to access private attribute
def set_age(self, age):
if age > 0:
self._age = age
else:
raise ValueError("Age must be positive")
|
Abstraction
Abstraction is the concept of hiding the complex implementation details and showing only the necessary features of an object
Magic Methods or Dunder Methods
Magic methods are pre-defined methods in Pyhton that you can override to change the behavior of your objects.
__init__
__str__
__repr__
__len__
__getitem__
__setitem__
| class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Person(name={self.name}, age={self.age})"
def __repr__(self):
return f"Person(name={self.name}, age={self.age})"
bob = Person("Bob", 30)
print(bob) # Calls __str__
print(repr(bob)) # Calls __repr__
|
Person(name=Bob, age=30)
Person(name=Bob, age=30)
Operator overloading
Operator overloading allows you to define the behavior of operators (+, -, *, ...) for custom objects
__add__
: add (+)
__sub__
: subtraction(-)
__mul__
: multiplication (*)
__truediv__
: division (/)
__eq__
: equals (==)
__lt__
: less than (<)
| class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
def __str__(self):
return f"Vector({self.x}, {self.y})"
def __eq__(self, other):
return self.x == other.x and self.y == other.y
v1 = Vector(2, 3)
v2 = Vector(5, 7)
v3 = v1 + v2
print(v3)
v4 = v2 - v1
print(v4)
print(v1 == v2)
print(v1 == Vector(2, 3))
|
Vector(7, 10)
Vector(3, 4)
False
True
Custom Exceptions
| class Error(Exception):
"""Base class for custom exceptions."""
pass
class ValidationError(Error):
"""Raised when a validation error occurs."""
def __init__(self, message):
self.message = message
super().__init__(self.message)
|