Is-A Relationship: ================== ==> Inheritance can define "Is-A relationship" between classes. Ex: --- class A: attributes methods class B(A): attributes methods here: B(A) ==> Class B is Class A class Vehicle: attributes methods class Car(Vehicle): attributes methods Here: Car is a Vehicle Has-A Relationship: =================== class A: class B: b = B() => when a reference variable of one class can be defined within the another class that relationship is called as "Has-A relationship". => Also called as "Association". Ex: class Vehicle: class Car: def __init__(self): car = Car() Ex: ---- class Policy: class Customer: policyId customerId policyholder = Customer() customerName here: Policy Has-A Customer => Has-A relationship can be either: aggregation composition Ex: class University: class Department: dept = Department() here: Department cannot functioning without University strong bonding between University and Department that relation is called as "Composition". Ex: ---- class Policy: class Customer: policyId customerId policyholder = Customer() customerName here: Policy Has-A Customer Customer can functioning without Policy also weak bonding between Policy and Customer This relation is called as "Aggregation". =================================================================== Data Abstraction: ================= Abstract method ---------------- => Sometimes, we don't know about the implementation of method but still we can declare a method. Such type of method called as "Abstract method". => Abstract method has only the declaration but not with implementation. => To define an abstract method in python we can use "@abstractmethod" decorator. Ex: @abstractmethod def m1(self): pass => @abstractmethod decorator is from abc module. Before to use we must be import the abc module. If not we will get error Ex: ---- from abc import * class Test: @abstractmethod def m1(self): pass Ex: --- from abc import * class Fruit: @abstarctmethod def taste(self): pass Note: ----- Child classes are responsible to provide the implementation for parent class abstract method. Abstract class -------------- => Sometimes implementation of class is not complete such type of partially implemented classes are called as "Abstract classes". => Every abstract class in python should be derived from ABC class which is present in abc module. Ex: ---- from abc import * class Test: pass t = Test() => here I have created an object for "Test" class because this is concrete class and it does not have any abstract methods. Ex: ---- from abc import * class Test(ABC): pass t = Test() => here also we can able to create an object even the class is derived from "ABC" because: it has no abstract methods. Ex: --- from abc import * class Test(ABC): @abstarctmethod def m1(self): pass t = Test() ==> Type error Ex: ---- from abc import * class Test: @abstarctmethod def m1(self): pass t = Test() ==> not an error because the Test class was not extending ABC class. Ex: ---- from abc import * class Test: @abstractmethod def m1(self): print("Hello") t = Test() t.m1() Interface ---------- => Interface is a class in python An abstract class contains only abstract methods such type of abstract class is called as "Interface". from abc import * class DBInterface(ABC): @abstractmethod def connect(self):pass @abstractmethod def disconnect(self):pass class Oracle(DBInterface): def connect(self): print("Connecting to Oracle Database...") def disconnect(self): print("Disconnecting to Oracle Database...") dbName = input("Enter Database Name:") className = globals()[dbName] x = className() x.connect() x.disconnect() ====================================================================== Do we overload the constructor? ------------------------------- Yes, we do overload the constructor according to the definition. But as per the expectation the object of the class cannot invoke both the constructor definitions. We cannot implement constructor overload. class MyClass: def __init__(self): print("It is parameter-less constructor") def __init__(self,x): self.x = x print("x = ",self.x) print("It is parameterized constructor") # mc1 = MyClass() mc2 = MyClass(100) How to access private variables from outside of the class? ---------------------------------------------------------- Syntax: object-name._Class-Name__private-variable-name class Test: def __init__(self): self.__x = 100 # private variable t = Test() # print(t.__x) print(t._Test__x) ============================================================================= __str__() method: ------------------ class Student: def __init__(self,name,roll): self.name = name self.roll = roll def printDetails(self): print("Name = ",self.name) print("Roll = ",self.roll) def __str__(self): return "This is Student with Name : {} and Roll : {}".format(self.name,self.roll) stu = Student("Ashish",101) stu.printDetails() stu1 = Student("Santosh",121) stu2 = Student("Durga Chaitanya",123) print(stu) print(stu1) print(stu2) ====================================================================================== Mini Project: ------------- class Account: def __init__(self,name, balance,min_balance): self.name = name self.balance = balance self.min_balance = min_balance def deposit(self, amount): self.balance += amount def withdraw(self,amount): if self.balance-amount >= self.min_balance: self.balance -= amount else: print("Sorry, Insufficient funds") def printStatement(self): print("Account Balance = ",self.balance) class Current(Account): def __init__(self,name,balance): super().__init__(name,balance,min_balance = -1000) def __str__(self): return "{}'s Current Account with Balance : {}".format(self.name,self.balance) class Savings(Account): def __init__(self,name,balance): super().__init__(name,balance,min_balance = 0) def __str__(self): return "{}'s Savings Account with Balance : {}".format(self.name,self.balance) sa = Savings("Rakesh",15000) print(sa) sa.deposit(25000) sa.printStatement() sa.withdraw(10000) sa.printStatement() print(sa) print("------------------------------------------") ca = Current("Nilesh",6500) print(ca) ca.deposit(250000) ca.printStatement() ca.withdraw(100000) ca.printStatement() print(ca) =================================================================== Inner Classes: -------------- class inside another class is called as "Inner classes". => Without existing one type of object if there is no chance of existing another type of object then we should go for inner classes. Ex-1: ----- class Outer: def __init__(self): print("Outer class Constructor") class Inner: def __init__(self): print("Inner class constructor") def m1(self): print("Inner class method") out = Outer() inn = out.Inner() inn.m1() Ex-2: ----- class Person: def __init__(self): self.name = "Niteesh" self.db = self.Dob() def display(self): print("Name = ",self.name) class Dob: def __init__(self): self.dd = 20 self.mm = 12 self.yy = 1998 def display(self): print("DOB = {}-{}-{}".format(self.dd,self.mm,self.yy)) p = Person() p.display() dob = p.db dob.display()